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

Spring Boot OpenID Connect中Refresh Token续期AccessToken的实现咨询

Spring Boot + OpenID Connect:Refresh Token续期与会话令牌替换指南

嘿,作为刚上手Spring Boot和OIDC的新手,你碰到的这俩问题其实特别典型——好在Spring Security已经内置了不少OIDC相关的能力,不用我们从头写复杂逻辑。我来一步步给你拆解解决方案:

一、用Refresh Token续期AccessToken:不用自己写核心逻辑

Spring Security的OIDC客户端模块已经集成了Refresh Token的自动处理,你只需要确保几个配置到位就行:

1. 先让客户端请求offline_access权限

OIDC规范里,只有当你请求offline_access这个scope时,身份提供商才会返回Refresh Token。所以在你的配置文件(比如application.yml)里要加上这个scope:

spring:
  security:
    oauth2:
      client:
        registration:
          my-oidc-client:  # 替换成你的客户端ID
            client-id: your-client-id-here
            client-secret: your-client-secret-here
            scope: openid, profile, email, offline_access  # 必须包含offline_access
            # 其他登录相关配置(比如redirect-uri)
        provider:
          my-oidc-provider:  # 替换成你的提供商ID
            issuer-uri: https://your-oidc-provider.com/issuer  # 比如Keycloak、Auth0的地址

2. 直接用Spring Security的服务获取有效令牌

当AccessToken过期时,OAuth2AuthorizedClientService会自动帮你用Refresh Token去刷新。如果你需要手动获取或检查令牌有效性,可以这么写:

@Autowired
private OAuth2AuthorizedClientService authorizedClientService;

public String getValidAccessToken(Authentication authentication) {
    // 加载当前用户的授权客户端信息
    OAuth2AuthorizedClient client = authorizedClientService.loadAuthorizedClient(
        "my-oidc-client", authentication.getName()
    );
    
    // 可以提前检查令牌是否即将过期(比如剩余时间不足60秒),手动触发刷新
    if (client.getAccessToken().expiresIn() < 60) {
        OAuth2AuthorizedClient refreshedClient = authorizedClientService.refreshAuthorizedClient(
            "my-oidc-client", authentication
        );
        return refreshedClient.getAccessToken().getTokenValue();
    }
    
    // 没过期的话直接返回当前令牌
    return client.getAccessToken().getTokenValue();
}

二、是否需要自定义过滤器?怎么替换会话里的Authentication?

先说结论:大部分场景下不需要自定义过滤器——Spring Security会在每次请求时自动检查AccessToken有效性,过期且有有效Refresh Token的话会自动刷新。但如果你的业务需要主动定期检查(比如后台任务),或者要把新令牌同步到你自定义的UsernamePasswordAuthenticationToken里(正常OIDC认证后会话里是OAuth2AuthenticationToken,你应该是做了自定义转换?),可以按下面的步骤来:

1. 自定义令牌刷新过滤器(如果需要主动检查)

写一个继承OncePerRequestFilter的过滤器,放到Spring Security过滤器链里,在OIDC认证过滤器之后执行:

@Component
public class TokenRefreshFilter extends OncePerRequestFilter {

    @Autowired
    private OAuth2AuthorizedClientService authorizedClientService;
    
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        
        // 只处理OIDC类型的认证对象
        if (auth instanceof OAuth2AuthenticationToken) {
            OAuth2AuthenticationToken oauthAuth = (OAuth2AuthenticationToken) auth;
            String clientId = oauthAuth.getAuthorizedClientRegistrationId();
            String username = oauthAuth.getName();

            OAuth2AuthorizedClient client = authorizedClientService.loadAuthorizedClient(clientId, username);
            // 提前5分钟检查令牌是否即将过期
            if (client != null && isTokenAboutToExpire(client.getAccessToken())) {
                // 触发令牌刷新
                OAuth2AuthorizedClient refreshedClient = authorizedClientService.refreshAuthorizedClient(clientId, auth);
                
                // 如果需要替换会话里的UsernamePasswordAuthenticationToken
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken newAuthToken = new UsernamePasswordAuthenticationToken(
                    userDetails,
                    null,  // OIDC认证不需要凭证,填null就行
                    userDetails.getAuthorities()
                );
                // 复制原认证对象的细节(比如请求信息)
                newAuthToken.setDetails(oauthAuth.getDetails());
                
                // 更新SecurityContext和会话,确保后续请求用新的认证对象
                SecurityContextHolder.getContext().setAuthentication(newAuthToken);
                request.getSession().setAttribute(
                    HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
                    SecurityContextHolder.getContext()
                );
            }
        }
        
        // 继续执行过滤器链
        filterChain.doFilter(request, response);
    }

    // 判断令牌是否即将过期
    private boolean isTokenAboutToExpire(OAuth2AccessToken accessToken) {
        Instant now = Instant.now();
        // 提前5分钟触发刷新,避免刚好在请求过程中过期
        return accessToken.getExpiresAt().minus(Duration.ofMinutes(5)).isBefore(now);
    }
}

2. 把过滤器加到Spring Security配置里

在你的SecurityFilterChain配置中,将这个过滤器放在OAuth2AuthenticationProcessingFilter之后:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private TokenRefreshFilter tokenRefreshFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .anyRequest().authenticated()
            )
            .oauth2Login(oauth2 -> oauth2
                // 你的OIDC登录页面、回调地址等配置
            )
            .oauth2Client(withDefaults())
            // 添加自定义过滤器,放在OIDC认证过滤器之后
            .addFilterAfter(tokenRefreshFilter, OAuth2AuthenticationProcessingFilter.class);
        
        return http.build();
    }
}

几个关键提醒

  • Refresh Token有效期:这个是由你的OIDC提供商决定的,比如Keycloak默认Refresh Token有效期是30天。如果Refresh Token也过期了,那就只能让用户重新登录了。
  • 别过度自定义:Spring Security已经帮我们处理了大部分令牌刷新的场景,只有当你有特殊业务需求(比如主动后台刷新)时才需要写自定义过滤器。
  • 会话同步要到位:替换Authentication对象后,一定要同时更新SecurityContext和HttpSession,不然后续请求可能还是用旧的令牌信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:18:06