无状态的 Spring Security oauth2 提供者

Posted

技术标签:

【中文标题】无状态的 Spring Security oauth2 提供者【英文标题】:Stateless spring security oauth2 provider 【发布时间】:2016-11-09 20:47:35 【问题描述】:

我想基于 spring-boot、spring-security 和 spring-oauth2 设置一个简单的 OAuth2 提供程序。

我在单实例机器上完成了所有工作:对于 OAuth2 授权,用户被发送到 /oauth/authorize。大多数用户没有登录,所以他们被spring security重定向到/login,然后返回/oauth/authorize以完成授权。

在默认配置中,spring-security 在用户浏览器中设置一个带有 session-id 的 cookie,并将会话数据存储在内存中。

public static class SecurityConfiguration extends WebSecurityConfigurerAdapter 
    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
            .authorizeRequests()
            .and()
            .formLogin()
            .loginPage("/login")
            .permitAll();
    
[...]

为了在不丢失用户会话的情况下启用负载平衡和蓝绿部署,(我认为)我必须执行以下步骤:

禁用服务器端会话 - 对于只负责 OAuth2 授权的 API,我认为没有必要为会话提供共享数据库。 相反,在授权期间启用一个包含用户身份验证的记住我的 cookie。 将 /login 重定向的重定向 URL 存储在不同的位置 是否可以将其存储在登录表单或用户 cookie 中?或者什么是“无会话”替代方案? 禁用 CSRF(我知道该怎么做,并且 oauth2 有 auth_codes,我认为它具有类似的目的。只是为了完整性。)

这种方法有意义吗?需要进行哪些更改?

【问题讨论】:

我大致做的是:(a)禁用 CSRF(b)在 cookie 中存储重定向 url。我只需要存储 oauth2 代码。 【参考方案1】:

这是分布式会话存储的经典问题。 首先,“会话”(会话 id 和 cookie)的概念与“无状态”相结合是一种矛盾。

OAuth2 应该是“无状态”委托授权框架提供您在生成访问代码之前在服务器端保留初始输入请求(包括重定向 url) .

在接收凭据之前将这些详细信息泄露给 cookie 可能会使您面临安全漏洞。您可以通过确保 cookie 是 HttpOnly(JS 无法访问)和 secure(仅通过 httpS 发布)来降低风险,但无论如何我都不推荐这种方法.

关于您的另一点:Spring Security 的记住我功能旨在仅携带对身份验证凭据的引用,而不是初始 auth2 请求的详细信息。此外,持久化选项(PersistentTokenBasedRememberMeServices)默认只支持内存(单节点)和jdbc两种风格。

根据您的需要调整这些将需要进行相当大的更改。可行,但需要付出很多努力。

根据我的经验,我想到了两种选择:

    使用前端负载均衡器配置粘性会话(例如:haproxy、nginx、F5 等...)。用户会话将绑定到提交凭据的节点。 这意味着如果该节点出现故障;用户必须重新进行身份验证才能创建新的访问令牌,但如果用于其他节点,已经给出的访问令牌应该没问题。

    配置/实现透明的分布式网络会话存储。 一些分布式内存存储供应商(例如:hazelcast)提供plugins,它们被配置到应用程序服务器上,以使这对用户透明。这意味着一些额外的开销,但几乎不需要额外的代码来满足您的要求。

【讨论】:

【参考方案2】:

    用户登录您的提供商后,您会生成一个授权代码,该代码将发送到客户端应用程序(通过重定向(回调)url)。

    稍后,客户端应用程序向您的服务器发出请求以获取访问令牌。在此请求中,它提供了自动化代码。

    此时,您需要能够将客户端应用程序发送的授权码与您首先生成的授权码进行比较。这就是您需要共享内存的地方。

如果您查看协议section-4.1 的这一部分,您需要点 C 和 D 之间的共享内存。

这不能通过您的服务器之外的任何东西来实现,因为这是您验证客户端应用程序是否被授权的点。

在此过程中稍后的访问和刷新令牌也是如此。

对于登录步骤(A 点和 B 点) - 在登录表单中包含重定向 url(和客户端状态 - 参见第 4.1 节)看起来很好。如果这是唯一使用会话的地方 - 你可以摆脱它。但是您仍然需要共享内存(共享数据库)作为授权代码。

【讨论】:

嗨,Alex,您描述了 OAuth2 流程,但没有回答我的问题。抱歉,我不能为此给你赏金。 是的,不知道在 spring-security 的上下文中你应该做什么的细节,但我的意思是无论如何你都需要一个共享数据库。 好吧,有道理,我实际上确实将授权码存储在共享数据库中,因此该部分存在。但我想看看我是否可以摆脱一般的会话存储。 是的,您可以在登录过程中摆脱它

以上是关于无状态的 Spring Security oauth2 提供者的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Spring Security 的 CSRF 功能用于无状态端点?

Spring Security 和无状态的 Restful 服务

使用 Spring Security Saml 和 SP 应用程序的无状态会话

wso2 spring security saml 无状态集成

如何在 Spring Security 无状态(jwt 令牌)中注销用户?

springCloud-依赖Spring Security使用 JWT实现无状态的分布式会话