我们目前有4个Spring应用程序使用Spring Security Oauth2项目进行身份验证。这些应用程序是REST API,由我工作的公司中的其他内部应用程序使用。
由于我们没有进行负载平衡,所以在开发和QA环境中一切都运行良好,现在我们正在进行预生产,我们正面临负载均衡器(LB)的问题。
这是此问题的工作流程:
我们正在使用内存用户存储:
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />
有没有办法让不同的盒子共享同一个令牌存储?我知道有一个JdbcTokenStore可用于将令牌持久化到数据库,但我宁愿避免持久令牌,因为这些应用程序指向仅存储业务信息的旧数据库。
答案 0 :(得分:3)
所有身份验证服务器和所有资源服务器必须共享相同的tokenStore
才能验证令牌。
这意味着切换到JdbcTokenStore
或自定义TokenStore
实现,该实现能够以某种方式在服务器之间共享令牌(共享数据存储,NFS共享文件系统等)。当然,如果您愿意使用Terracotta或类似的记忆共享产品,甚至可以共享InMemoryTokenStore
。
答案 1 :(得分:3)
我对这个问题有点迟了,但也许这会帮助搜索类似答案的人。在负载均衡器上使用多个oauth服务器时,需要注意两件事:
正如@ chris-h在他的回答中提到的那样,您需要确保任何其他oauth服务器都可以读取(和信任)任何oauth服务器发出的访问令牌的信息。您可以按照他的建议使用JDBC令牌存储,但这样做的缺点是,如果服务器A必须验证服务器B发出的访问令牌,它总是必须命中数据库才能这样做。
更好的解决方案(IMO)是使用JWT访问令牌,其中验证令牌所需的所有信息都在其中加密。只要所有oauth服务器使用相同的加密密钥,他们就可以读取彼此访问令牌中的数据,并相信数据是有效的,因为它是加密的。优点是验证访问令牌不需要数据库调用。缺点是,一旦发出访问令牌,就没有简单的方法使访问令牌无效。如果你想知道为什么只需要增加访问令牌本身的到期时间就需要刷新令牌,这就是最大的原因。
要注意的第二件事是Spring oauth实现使用会话来跟踪用户在身份验证过程中的位置。如果你不小心,你可以在一个无限循环中结束#34;场景。假设您有两个oauth服务器 - 服务器A和服务器B:
此问题的最佳(当前)解决方案可能是确保您的负载均衡器支持&#34;粘性会话&#34; - 也就是说,一旦它将特定用户发送到服务器A或服务器B,它总是将该用户暂时发送到同一服务器。
更好的解决方案可能是oauth实现根本不使用会话。相反,使用作为参数传递的加密数据到/ oauth / *,表示您在登录过程中的位置。与JWT令牌的工作方式类似,如果所有服务器共享加密密钥,则可以信任该信息。
答案 2 :(得分:0)
专门用于实现授权代码授予以与负载均衡器一起工作,我将Spring会话存储在Redis中。 Redis是共享资源,可确保Spring会话信息在所有正在运行的Spring应用程序实例之间共享。
还有一种替代方法,可以通过在负载均衡器处设置粘性会话来评估,这也是一个不错的选择,但是此实现需要会话复制才能维护HA。
IMHO集中式高速缓存存储实现可在应用程序端进行更多控制,使应用程序具有所有配置,并保证HA,而无需任何额外开销。
以下是会话在Redis中的存储方式
application.properties
spring.session.store-type=redis
server.servlet.session.timeout=3600s
spring.session.redis.flush-mode=on-save
spring.session.redis.namespace=spring:session
用于覆盖HttpSession管理
@Configuration
@EnableRedisHttpSession
public class HttpSessionConfig extends AbstractHttpSessionApplicationInitializer {
@Bean
public JedisConnectionFactory redisConnectionFactory() {
...
JedisConnectionFactory jedisConFactory = new JedisConnectionFactory(redisConfig);
...
return jedisConFactory;
}
}