You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

集群环境下Spring Authorization Server令牌续期:Spring Session能否替代粘性会话?

Spring Authorization Server集群下令牌续期失效问题排查与解决方案

问题背景与现象

单实例Spring Authorization Server搭配独立客户端、资源服务器时,访问令牌自动续期功能正常;但部署为集群(Docker Compose + Nginx代理 + 2个授权服务器实例)后,续期功能失效:

  • 客户端调试发现:DefaultRefreshTokenTokenResponseClient.getResponse()调用http://localhost:9000/oauth2/token时,授权服务器重定向至http://localhost:9000/login,最终导致getTokenResponse()抛出NullPointerException:

    Cannot invoke "org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse.getAccessToken()" because "tokenResponse" is null

  • 授权服务器已集成Spring Session(会话存储至数据库),并采用Rob Winch实现的密钥轮换策略(密钥存储至数据库)
  • 客户端已配置HttpSessionOAuth2AuthorizedClientRepository:
    @Bean
    public OAuth2AuthorizedClientRepository authorizedClientRepository() {
        return new HttpSessionOAuth2AuthorizedClientRepository();
    }
    
    且能在SPRING_SESSION_ATTRIBUTES中看到org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository.AUTHORIZED_CLIENTS,但客户端仍运行失败
  • 配置Nginx ip_hash粘性会话后问题解决,但期望通过Spring Session实现无粘性会话的集群部署

问题根源分析

Spring Session本身完全适配Spring Authorization Server,出现该问题并非框架不适配,而是配置遗漏或错误:

  1. /oauth2/token端点未允许匿名访问:刷新令牌流程是无状态的,基于客户端凭证和刷新令牌完成,不需要用户会话。若集群中某个实例未配置该端点匿名访问,会触发登录重定向。
  2. 刷新令牌未配置分布式存储:默认情况下,刷新令牌存储在内存中,集群实例间不共享。当请求落到没有该刷新令牌的实例时,验证失败会触发认证拦截。
  3. Nginx代理丢失关键请求头:转发时未保留Authorization、Cookie等头信息,导致授权服务器无法识别客户端凭证或会话信息,进而触发重定向。

解决方案

1. 确保/oauth2/token端点允许匿名访问

在授权服务器的SecurityFilterChain中明确配置:

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    http
        .exceptionHandling(exceptions -> exceptions
            .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
        )
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/oauth2/token").permitAll()
            .anyRequest().authenticated()
        );
    return http.build();
}

2. 配置刷新令牌的分布式存储

将刷新令牌存储到共享数据库或Redis,以实现集群实例间共享:

JDBC存储示例

引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-authorization-server-jdbc</artifactId>
</dependency>

配置授权服务使用JDBC存储:

@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
    return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}

@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
    return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}

3. 修正Nginx代理配置

确保转发时保留关键请求头:

http {
  upstream loadbalancer {
    server authz1:9000;
    server authz2:9000;
  }

  server {
    listen 9000;
    location / {
      proxy_pass http://loadbalancer;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header Cookie $http_cookie;
    }
  }
}

4. 验证Spring Session配置一致性

确保所有授权服务器实例的Spring Session配置完全一致:

  • 依赖引入正确(如spring-boot-starter-data-redis或spring-boot-starter-jdbc)
  • 数据库/Redis连接配置统一
  • 会话序列化配置正确(避免跨实例会话无法反序列化)

是否需要放弃Spring Session改用粘性会话?

不需要。粘性会话只是规避问题的临时方案,会导致负载不均、单点故障等隐患。Spring Session完全支持Spring Authorization Server的集群部署,只要完成上述配置修正,就能实现无粘性会话的高可用集群。

内容的提问来源于stack exchange,提问作者Eduardo Guimaraes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 22:53:18