我想基于spring-boot,spring-security和spring-oauth2设置一个简单的OAuth2提供程序 .
我在一台实例计算机上运行了一切:对于OAuth2授权,用户被发送到 /oauth/authorize
. 大多数用户都没有登录,所以他们被 spring 安全重定向到 /login
然后返回t /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();
}
[...]
为了在不丢失用户会话的情况下启用负载 balancer 和蓝绿色部署,(我认为)我必须执行以下步骤:
-
禁用服务器端会话 - 对于仅负责OAuth2授权的API,我认为没有必要为会话 Build 共享数据库 .
-
相反,在授权期间启用包含用户身份验证的记住我的cookie .
-
将
/login
重定向的重定向网址存储在其他位置 -
是否可以将其存储在登录表单或用户cookie中?或者什么是"sessionless"替代品?
-
禁用CSRF(我知道如何做到这一点,oauth2有auth_codes,我认为它具有类似的目的 . 只是为了完整性 . )
这种方法有意义吗?需要做哪些改变?
2 回答
这是分布式会话存储的典型问题 . 首先,“会话”(会话ID和cookie)与“无国籍”相结合的概念是一种矛盾 .
OAuth2应该是一个“无状态”委托授权框架 provided 您在生成访问代码之前,在服务器端保留初始输入请求(包括重定向URL) .
在接收凭据之前将这些详细信息泄露给cookie可能会让您面临安全漏洞 . 您可以通过确保cookie是 HttpOnly (JS无法访问)和 secure (仅通过httpS发布)来降低风险,但我不建议采用这种方法 .
关于您的另一点:Spring Security的remember-me功能仅用于提供对身份验证凭据的引用,而不是有关初始auth2请求的详细信息 . 此外,持久化选项(PersistentTokenBasedRememberMeServices)默认仅支持内存(单节点)和jdbc风格 .
根据您的需求调整这些需要进行大量更改 . 可行,但需要付出很多努力 .
根据我的经验,我想到了两种选择:
Configure sticky sessions using a front-load balancer (例如:haproxy,nginx,F5等......) . 用户会话将绑定到提交凭据的节点 . 这意味着如果该节点发生故障;用户必须重新进行身份验证才能创建新的访问令牌,但是如果用于其他节点,则已经给出的访问令牌应该没有问题 .
Configure/implement a transparent distributed web session storage. 某些分布式内存存储提供程序(例如:hazelcast)提供了plugins,它们被配置到应用程序服务器以使其对用户透明 . 这有一些额外的开销,但几乎没有额外的代码来满足您的要求 .
用户登录您的提供商后,您将生成一个授权代码,该代码将发送到客户端应用程序(通过重定向(回调)URL) .
稍后,客户端应用程序向服务器发出请求以获取访问令牌的请求 . 在此请求中,它提供了自动化代码 .
此时,您需要能够将客户端应用程序发送的授权代码与您首先生成的授权代码进行比较 . 这是您需要共享内存的地方 .
如果你看一下协议section-4.1的这一部分,你需要C点和D点之间的共享内存 .
使用服务器之外的任何内容都无法实现这一点,因为这是验证客户端应用程序是否经过授权的关键点 .
类似于稍后在该过程中访问和刷新令牌的情况 .
对于登录步骤(点A和B) - 在登录表单中有重定向URL(和客户端状态 - 请参阅第4.1节)看起来很好 . 如果这是使用会话的唯一地方 - 你可以摆脱它 . 但是您仍然需要共享内存(共享数据库)来获取授权代码 .