如何使用 oAuth2 对 SPA 用户进行身份验证?
Posted
技术标签:
【中文标题】如何使用 oAuth2 对 SPA 用户进行身份验证?【英文标题】:How to authenticate SPA users using oAuth2? 【发布时间】:2017-05-20 16:45:20 【问题描述】:好的,我花了几天时间寻找合适的解决方案,了解如何在使用 SPA 时正确验证用户身份。
-
我有自己的网站。
我有自己的 API。
我有自己的单页应用程序。
我有自己的用户数据库。
目标:我需要通过提供用户名和密码来获取 access_token。
我查看了 OAuth2 Implicit Grant,但它要求用户在成功验证后批准/拒绝该应用程序。它在我的情况下不起作用,因为我拥有应用程序和 API。
我查看了 OAuth2 Password Grant,它并不完美,因为我需要公开 client_id/client_secret。
我之所以关注 OAuth2,是因为 API 最终会公开。
有没有一种标准方法可以做到这一点?我目前的选择:
-
忘记 OAuth2 并在用户 POST 用户名/密码时手动生成 access_token(在这种情况下,我必须在 API 公开时引入 OAuth2)
使用 OAuth2 Password Grant 并在服务器上注入 client_id/client_secret,因此只是为了保持客户端应用程序非常简单(同时避免所有那些 dev/staging/prod client_id/client_secret 对)
【问题讨论】:
OAuth 适用于您不拥有至少一个需要调用的资源 (API) 并且需要向用户请求权限以在他/她上使用这些资源的情况代表。在我看来,您只需要微服务之间的主体传播。 您使用的是特定的 javascript 框架还是其他开发环境? Visual Studio 2017 中的 SPA 模板对此有一个可行的解决方案,也许看看那个? 【参考方案1】:隐式授予
您是对的,隐式授权类型看起来不合适。但是我认为您不赞成它的原因是不正确的,因为批准步骤不是强制性的,并且在 Spring OAuth 2 实现中(我不知道您使用的是哪个实现),您可以将授权服务器配置为自动批准授权请求,以便跳过审批步骤。
我认为“隐式流”不合适的原因是
-
缺少提供客户端密码和授权码的客户端身份验证步骤。所以安全性较低。
访问令牌作为 URL 片段发回(这样令牌不会发送到服务器),该片段将继续保留在浏览器历史记录中
如果发生 XSS 攻击,恶意脚本可以很好地将令牌发送到远程服务器
资源所有者密码凭证授予
如果授权服务器和资源服务器是相同的,我认为这是一个快速启动和运行的方法。 RFC 6749 在第 4.3.2 节中说:
如果客户端类型是机密的或客户端已获得客户端凭据(或分配了其他身份验证要求),则客户端必须按照第 3.2.1 节所述向授权服务器进行身份验证。
这意味着使用客户端密码的客户端身份验证在这里不是强制性的。现在,对于授权码授权类型,我们需要客户端密码,因为用户直接将他/她的凭据提供给授权服务器,然后当客户端请求访问令牌时,除了客户端密码之外没有其他任何东西可以向授权服务器证明这是一个真实的请求。
但在资源所有者密码凭据授予类型的情况下,用户已将其凭据提供给客户端本身,然后客户端将发送这些相同的用户凭据以请求访问令牌。因此,访问令牌请求只能使用用户凭据进行身份验证,如果我们在这里不提供客户端密码,我认为我们不会在安全性方面失去任何东西。
因此,您绝对可以在您的 SPA 中使用密码凭据授予类型。
授权码授予
如果客户端密码未存储在浏览器中,我认为这应该是首选选项。在用户身份验证(以及可选的用户批准)之后,授权服务器可以将浏览器重定向到带有 URL 中授权代码的服务器端端点。服务器端端点将使用授权码、客户端 ID 和客户端密码(仅存储在服务器端)请求访问令牌。一旦访问令牌可用,服务器端端点可以使用适当的 cookie 将用户重定向(HTTP 响应代码 302)到 SPA URL,用于 CSRF 保护和访问令牌。因此,我们不会将客户端密码存储在浏览器中。
通过使用授权码授权类型,您基本上可以使解决方案更加安全和通用。将来,如果您想使用不同的 SPA 进行单点登录,您可以通过重用与身份验证数据库(最好是 LDAP 服务器)集成的同一授权服务器来轻松实现。
更多详情,请参考我的*** answer here。
【讨论】:
以上是关于如何使用 oAuth2 对 SPA 用户进行身份验证?的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在不重定向到外部登录页面的情况下进行 SPA 身份验证
Spring Boot Security - 如果用户未通过 Oauth2 进行身份验证,如何禁用对 swagger ui 页面的端点的访问