在不违反 RESTful 原则的情况下,在 Angular 中进行身份验证和授权的最佳实践?
Posted
技术标签:
【中文标题】在不违反 RESTful 原则的情况下,在 Angular 中进行身份验证和授权的最佳实践?【英文标题】:Best practices for authentication and authorization in Angular without breaking RESTful principles? 【发布时间】:2014-04-24 16:00:12 【问题描述】:我已经阅读了很多关于使用 REST 和 Angular 进行身份验证和授权的 SO 主题,但我仍然觉得我没有一个很好的解决方案来解决我希望做的事情。对于一些背景,我计划在我想要支持的 AngularJS 中构建一个应用程序:
-
访客访问受限
通过身份验证后对应用程序的基于角色的访问
通过 API 进行身份验证
所有对 REST API 的调用都需要通过 SSL 进行。我想在不违反 RESTful 原则的情况下构建应用程序,即不将会话状态存储在服务器上。当然,在客户端对授权所做的任何事情都必须在服务器端得到加强。由于我们需要通过每个请求传递整个状态,我知道我需要传递某种令牌,以便接收 REST 请求的后端服务器可以对调用进行身份验证和授权。
话虽如此,我的主要问题是关于身份验证 - 这里的最佳做法是什么?似乎讨论了很多不同的方法,这里只是我发现的一些:
http://broadcast.oreilly.com/2009/12/principles-for-standardized-rest-authentication.html http://frederiknakstad.com/2013/01/21/authentication-in-single-page-applications-with-angular-js/ http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html问了一个类似的问题 (AngularJS best practice application authentication),但除非我误解了答案,否则似乎暗示应该使用服务器会话,这违反了 RESTful 原则。
我对 Amazon AWS 和 George Reese 文章的主要关注是它似乎假设消费者是一个程序,而不是最终用户。可以提前将共享密钥发布给程序员,然后程序员可以使用它在这里对调用进行编码。这不是这里的情况 - 我需要代表用户从应用程序调用 REST API。
这种方法就足够了吗?假设我有一个会话资源:
POST /api/session
为用户创建一个新会话
要创建会话,您需要 POST 包含“用户名”和“密码”的 JSON 对象。
"email" : "austen@example.com",
"password" : "password"
卷曲示例
curl -v -X POST --data '"username":"austen@example.com","password":"password"' "https://app.example.com/api/session" --header "Content-Type:application/json"
回应
HTTP/1.1 201 Created
"session":
"id":"520138ccfa4634be08000000",
"expires":"2014-03-20T17:56:28+0000"
状态代码
201 - 已创建,新会话已建立 400 - 错误请求,JSON 对象无效或缺少所需信息 401 - 未经授权,检查电子邮件/密码组合 403 - 拒绝访问、禁用帐户或许可证无效为了清楚起见,我将省略 HATEOAS 的详细信息。在后端,将创建一个新的、持续时间有限的会话密钥并与用户关联。在后续请求中,我可以将其作为 HTTP 标头的一部分传递:
Authorization: MyScheme 520138ccfa4634be08000000
然后后端服务器将负责从请求中提取此内容,查找关联的用户并执行请求的授权规则。它可能也应该更新会话的到期时间。
如果这一切都发生在 SSL 上,我是否会对我应该防范的任何类型的攻击敞开大门?您可以尝试猜测会话密钥并将它们放在标题中,所以我想我可以另外将用户 GUID 附加到会话密钥以进一步防止暴力攻击。
我已经有好几年没有主动编程了,我才刚刚回到这里。抱歉,如果我是迟钝或不必要地重新发明***,只是希望根据我目前的阅读情况在这里的社区运行我的想法,看看他们是否通过了试金石。
【问题讨论】:
不完全相关,但这可能会让您感兴趣:youtube.com/watch?v=62RvRQuMVyg(第一部分处理身份验证,它们生成 index.html 服务器端并在其中包含配置文件,我在我的应用) 感谢分享,我稍后会检查一下! 【参考方案1】:这是一篇关于使用 Angular 构建的身份验证和登录服务的令人难以置信的文章。
https://medium.com/opinionated-angularjs/7bbf0346acec
【讨论】:
这是一篇很棒的文章,我可能会使用所讨论的一些技术,但它确实没有回答我的主要问题,即登录后如何验证后续请求(除非我遗漏了什么)。验证/授权服务器端所需的信息需要作为每个后续 REST 请求的一部分传递,才能真正实现无状态。从安全角度来看,我的上述攻击计划是否足够,还是有其他更好的技术?【参考方案2】:这个 SO question 很好地总结了我对 REST 的理解
Do sessions really violate RESTfulness?
如果您将令牌存储在会话中,您仍在服务器端创建状态(这是一个问题,因为该会话通常只存储在一个服务器上,这可以通过粘性会话或其他解决方案来缓解)。
我想知道您创建 RESTful 服务的原因是什么,因为也许这并不是一个大问题。
如果您在每个请求的正文中发送一个令牌(因为所有内容都使用 SSL 加密,这没关系),那么您可以让任意数量的服务器(负载平衡)为请求提供服务,而无需任何先前的状态知识。
长话短说,我认为以 RESTful 实现为目标是一个不错的目标,但在身份验证和验证授权方面,纯粹的无状态肯定会增加一层复杂性。
到目前为止,我已经开始考虑 REST 构建我的后端,使 URI 有意义并使用正确的 HTTP 动词,但仍然在会话中使用令牌以简化身份验证(不使用多个服务器时) )。
我阅读了您发布的链接,AngularJS 似乎只关注客户端,并且似乎没有在那篇文章中明确解决服务器问题,他确实链接到另一个(我不是 Node 用户所以如果我在这里的解释有误,请原谅我)但似乎服务器依赖客户端来告诉它它拥有什么级别的授权,这显然不是一个好主意。
【讨论】:
废话。您不知道该令牌在何处或如何进行身份验证。服务器可以很容易地将其移交给其他一些(RESTful)基础设施。令牌仅代表过期的租约。令牌并不意味着会话。会话不是特别安静,令牌......只是令牌。 非常真实的Will Hartung,我在这里解释的不是很好,把一些事情搞混了,有机会深入思考后我会尝试纠正。出于好奇,您能否详细说明您将如何生成、存储和验证令牌?在阅读了你所说的之后,我确实意识到有几种方法,我在以一种封闭的方式思考。【参考方案3】:当有人询问 REST 身份验证时,我遵从 Amazon Web Services 并基本上建议“这样做”。为什么?因为,从“大众智慧”的角度来看,AWS 解决了问题,被人们大量使用、大量分析和审查,这些人比大多数人更了解和关心什么是安全请求。安全是“不重新发明***”的好地方。在“肩膀要站”这件事上,你可以做得比 AWS 差。
现在,AWS 不使用令牌技术,而是使用基于共享机密和有效负载的安全哈希。它可以说是一个更复杂的实现(包括所有的规范化过程等)。
但它有效。
缺点是它要求您的应用程序保留个人共享机密(即密码),并且它还要求服务器可以访问该密码的纯文本版本。这通常意味着密码被加密存储,然后在适当的时候解密。与安全散列技术相比,这会导致密钥管理和服务器端的其他事情变得更加复杂。
当然,任何令牌传递技术的最大问题是中间人攻击和重放攻击。 SSL 自然地主要缓解了这些问题。
当然,您还应该考虑 OAuth 系列,它们有自己的问题,尤其是互操作性,但如果这不是主要目标,那么这些技术肯定是有效的。
对于您的应用程序,令牌租用不是什么大问题。您的应用程序仍需要在租约期限内运行,或者能够续订。为了做到这一点,它将需要保留用户凭据或重新提示他们。只需将令牌视为一流资源,就像其他任何东西一样。如果可行,请尝试将一些其他信息与请求相关联,并将其捆绑到令牌(浏览器签名、IP 地址)中,以强制执行某些局部性。
您仍然对(潜在的)重放问题持开放态度,相同的请求可能会被发送两次。对于典型的散列实现,时间戳是签名的一部分,可以将请求的生命周期括起来。在这种情况下,解决方法不同。例如,可以使用序列号或 GUID 发送每个请求,并且您可以记录该请求已经播放过,以防止再次发生。不同的技术。
【讨论】:
谢谢,威尔,这很有帮助! AWS 技术与更通用的 JSON Web Token (JWT, jwt.io) 非常相似。我认为 JWT 是更好的框架。以上是关于在不违反 RESTful 原则的情况下,在 Angular 中进行身份验证和授权的最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不违反命令和查询端分离的情况下基于元数据在 CQRS 中构建查询
如何在不违反 CSP 的情况下绑定剃须刀视图中的 onchange 等事件