使用 Token 保护 REST api
Posted
技术标签:
【中文标题】使用 Token 保护 REST api【英文标题】:Securing REST api with Token 【发布时间】:2014-12-28 13:07:40 【问题描述】:过去几天我一直在努力解决这个问题,但在网络上没有找到可靠、易于理解的解决方案。
我有一个 web 应用程序,它由一个 rest api 和一个使用它的表示层组成。表示层有一个登录表单,用户引入的数据被发送到 REST api,然后使用第三方服务对用户进行身份验证。这部分我已经设置好了。
我现在想要的是为每个后续请求引入基于令牌的身份验证,这样我就不必在每个请求上发送凭据,然后再次针对第三方服务进行身份验证。
所以基本上,使用 Spring Security (v. 3.1.3),我不知道如何创建可靠、安全的令牌,将其返回给请求者,然后根据所述令牌对请求进行身份验证。
你能指出我正确的方向吗?或者网上的一些例子?
如何生成令牌?如何将令牌返回给 客户? 您将如何在后续请求中发送令牌? 如何设置整个事情,以便第一次可以 以某种方式发送凭据(用户/通行证)然后是后续 请求只发送令牌? 如何根据令牌进行身份验证? 我见过令牌包含一些到期时间的实现。那么expiryTime用完后会发生什么?用户必须再次登录,即使他一直在发出请求?我应该在“幕后”更新令牌吗?【问题讨论】:
您需要一个 oAuth 实施。我回答了一个类似的问题here。 我相信 oAuth 可能有点矫枉过正。我在网上阅读了有关更简单的基于令牌的身份验证的信息,并且在陷入 oAuht 地狱之前我正在考虑这一点。无论如何,如果您有任何关于 oAuth 的好教程,可能也会有所帮助。 如果您对每个请求执行完整的 oAuth 工作流程,那将是矫枉过正。通常您只需设置一次并使用访问令牌发送所有请求。并在需要时刷新令牌。阅读this RFC。 对不起,RFC 对我来说太难了,太抽象了 :( 简而言之,我的意思是 oAuth 需要一次设置。然后每个请求都会附带一个访问令牌。 【参考方案1】:服务器端 REST 应用程序是有状态的还是无状态的?如果是有状态的,则不需要使用常规 HTTP 会话做任何特别的事情。只需开始使用 Spring Security,如果客户端和服务器已经在交换会话信息,受保护的 API 端点将开箱即用。唯一需要注意的是,如果您启用了 CSRF 保护,在这种情况下,您需要稍微调整客户端。详细信息在 Spring Security 文档中。
另一方面,如果 REST 应用程序是无状态的,您将不得不使用您提出的基于令牌的方法。有关详细信息,请参阅my answer to a similar post。如果您确实选择按照该答案中的步骤进行操作,那么您的问题的答案是:
令牌必须在服务器端生成。如果您浏览链接到我的答案的示例代码,您会看到我已经用基于缓存的基础架构替换了 Spring Security 的默认 HTTP 会话相关基础架构。在基于会话的实现中,唯一的会话标识符,即会话 ID,由 servlet 容器生成,而在自定义实现中,标识符,即令牌,是在应用程序中生成的。具体来说,我使用了SecureRandom
实例来生成强标记。
客户端将令牌作为 HTTP 标头发回。这可以保护 SSL 通道上的内容。
诀窍是从 Spring Security 实现四个非常简单的接口。我的示例应用程序包含完整的详细信息。整个过程不到一个小时。
对令牌的身份验证由 Spring Security 自动完成。我们只需要提供一个用于存储和检索令牌的实现,这很简单,只需要几行代码。
在我的示例应用程序中,我使用了具有滑动到期功能的可到期缓存来存储令牌。如果客户端定期发送请求,服务器会继续访问缓存以获取身份验证令牌,从而使令牌在缓存中保持活动状态。只有在不活动时间等于或超过缓存到期期限后,令牌才会从缓存中清除。
总体而言,基于令牌的身份验证/授权方法可以通过 Spring Security 轻松实现,并利用 EHCACHE 等缓存库。
【讨论】:
以上是关于使用 Token 保护 REST api的主要内容,如果未能解决你的问题,请参考以下文章
REST 后端的 Spring Security CSRF 保护 - 将 Synchronizer Token Pattern 传输到客户端
Python web 开发django rest framwork 的token 登录和原理
寻求建议以保护用 python-flask 编写的私有 REST API
Django Rest Framework 将不接受我的 CSRF 令牌
使用 rest_framework.authtoken.views.obtain._auth_token 时出现 CSRF 错误