在不违反RESTful原则的情况下,Angular中身份验证与授权的最佳实践是什么?

43

我已经阅读了许多关于使用REST和Angular进行身份验证和授权的SO线程,但仍然感觉对我希望完成的内容没有很好的解决方法。背景是,我计划在AngularJS中构建一个应用程序,我想支持以下功能:

  1. 有限的访客访问权限
  2. 一旦通过身份验证,应用程序的基于角色的访问权限
  3. 通过API进行身份验证

所有对REST API的调用都需要在SSL上进行。我想在不违反RESTful原则的情况下构建应用程序,即不在服务器上存储会话状态。当然,无论在客户端方面采取什么样的授权方式,都必须在服务器端得到强化。由于我们需要通过每个请求传递整个状态,我知道需要传递某种令牌,以便接收REST请求的后端服务器可以对调用进行身份验证和授权。

说到这里,我的主要问题是有关身份验证的最佳实践是什么?看起来有很多不同的方法,这里只列举了我发现的几种:

有一个类似的问题(AngularJS最佳实践应用程序身份验证),但除非我误解了回答,否则它似乎暗示应使用服务器会话,这违反了RESTful原则。

我对Amazon AWS和George Reese文章的主要担忧是,它似乎假定使用者是程序,而不是终端用户。共享密钥可以提前分配给程序员,然后他们可以使用它来在此处编码调用。但这里并非如此-我需要代表用户从应用程序调用REST API。

这种方法是否足够?假设我有一个会话资源:

POST /api/session

为用户创建新会话

要创建会话,您需要发布包含“用户名”和“密码”的JSON对象。

{
    "email" : "austen@example.com",
    "password" : "password"
}

Curl示例

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 到会话密钥中以进一步防止暴力攻击。

我已经有几年没有积极编程了,现在才重新开始。如果我表达不清或者不必要地重复造轮子,请见谅,我只是希望根据我的阅读经验与社区分享我的想法,并查看它们是否通过了实践检验。


不是特别相关,但这可能会引起您的兴趣:https://www.youtube.com/watch?v=62RvRQuMVyg(第一部分处理身份验证,他们在服务器端生成index.html并在其中包含个人资料,我在我的应用程序中使用相同的技术) - Busata
谢谢分享,我一定会稍后查看! - austrum
3个回答

16
当有人询问REST身份验证时,我会推荐使用亚马逊云服务并建议“这样做”。为什么?因为从“大众智慧”的角度来看,AWS解决了这个问题,被广泛使用、深入分析,并经过了比大多数人更了解和关心安全请求的人的验证。而且,在安全领域,“不重复造轮子”是一个好的选择。在“依靠他人的基础上”,你可以选择AWS。
现在,AWS不使用令牌技术,而是使用基于共享密钥和载荷的安全哈希。这可能是一个更加复杂的实现(包括所有的规范化过程等)。
但它有效。
缺点是需要你的应用程序保留人们的共享密码(即密码),并需要服务器访问该密码的明文版本。通常意味着密码已加密存储,然后根据需要解密。这将在服务器端与安全哈希技术相比带来更多的密钥管理和其他问题。
当然,任何令牌传递技术的最大问题是中间人攻击和重放攻击。SSL可以大部分地缓解这些问题。
当然,您还应考虑OAuth家族,它们有自己的问题,特别是在互操作性方面,但如果这不是主要目标,则这些技术肯定是有效的。
对于您的应用程序,令牌租赁并不是一个大问题。您的应用程序仍需要在租赁时间范围内运行,或者能够更新它。为了做到这一点,它将需要保留用户凭据或重新提示用户输入。只需将令牌视为一等资源,与其他任何资源一样。如果可行,尝试将一些其他信息与请求相关联并捆绑到令牌中(浏览器签名、IP地址),以强制执行一些地方性。

您仍然面临着(潜在的)重放问题,即同一请求可能会被发送两次。使用典型哈希实现,时间戳是签名的一部分,可以界定请求的生命周期。但在这种情况下解决方式不同。例如,每个请求都可以用序列号或GUID发送,并记录该请求已经被播放,以防止再次发生。有不同的技术可供使用。


谢谢,威尔,这非常有帮助! - austrum
2
AWS技术与更通用的JSON Web Token(JWT,jwt.io)非常相似。我认为JWT是更好的框架。 - user239558

9

4
这是一篇很棒的文章,我可能会使用其中讨论的一些技术,但它未能回答我的主要问题,即如何在登录后验证后续请求(除非我漏掉了什么)。服务器端需要传递用于身份验证/授权的信息作为每个后续REST请求的一部分,以实现真正无状态。从安全角度来看,我上面的攻击计划是否足够或是否有其他更好的技术? - austrum

0

这个SO问题很好地总结了我对REST的理解。

会话真的违反了RESTful吗?

如果你在会话中存储一个令牌,你仍然在服务器端创建状态(这是一个问题,因为该会话通常只存储在一个服务器上,可以通过粘性会话或其他解决方案来缓解这个问题)。

我想知道你创建RESTful服务的原因是什么,因为也许这并不是一个真正的大问题。

如果你在每个请求的主体中发送一个令牌(因为所有内容都使用SSL加密,所以这是可以的),那么你可以有任意数量的服务器(负载均衡)为请求提供服务,而无需事先了解状态。

长话短说,我认为追求RESTful实现是一个很好的目标,但纯粹无状态肯定会在身份验证和验证授权时增加额外的复杂性。

到目前为止,我已经开始构建我的后端时考虑了REST,制定了有意义的URI,并使用了正确的HTTP动词,但在身份验证方面仍然使用会话中的令牌,以简化认证过程(当不使用多个服务器时)。

我阅读了您发布的链接,AngularJS 的那个似乎只关注客户端,并没有在文章中明确涉及服务器,他确实链接到另一篇文章(如果我的理解有误,请原谅我,因为我不是 Node 用户),但似乎服务器依赖于客户端告诉它所拥有的授权级别,这显然不是一个好主意。


1
胡说八道。你不知道那个令牌在哪里或如何进行身份验证。服务器可能会将其直接移交给其他(RESTful)基础架构。该令牌仅代表一个到期的租约。令牌并不意味着会话。会话并不特别符合RESTful,令牌只是令牌。 - Will Hartung
非常正确的Will Hartung,我在这里解释得不太好,有些事情混淆了,我会尽力在深入思考后进行更正。出于好奇,您能详细说明一下如何生成、存储和验证令牌吗?在阅读了您的话后,我意识到有几种方法,而我之前想法比较固化。 - shaunhusain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接