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

如何在Spring Authorization Server中支持通过offline_access scope请求refresh_token?

实现Spring Authorization Server仅在请求offline_access时发放Refresh Token

核心思路

避免复制重写整个OAuth2AuthorizationCodeAuthenticationProvider,通过继承并重写关键逻辑的方式修改刷新令牌的发放条件,保留原Provider的核心功能,保证代码可维护性。

具体实现步骤

1. 自定义授权码认证Provider

继承OAuth2AuthorizationCodeAuthenticationProvider,重写authenticate方法,将原本仅检查客户端是否支持REFRESH_TOKEN授权类型的逻辑,改为同时验证授权范围是否包含offline_access:

import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AccessToken;
import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

public class CustomOAuth2AuthorizationCodeAuthenticationProvider extends OAuth2AuthorizationCodeAuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
                (OAuth2AuthorizationCodeAuthenticationToken) authentication;

        // 调用父类核心认证逻辑
        OAuth2Authorization authorization = doAuthenticate(authorizationCodeAuthentication);

        // 生成访问令牌(复用父类逻辑)
        OAuth2TokenContext tokenContext = OAuth2TokenContext.builder()
                .registeredClient(authorization.getRegisteredClient())
                .principal(authorizationCodeAuthentication.getPrincipal())
                .authorization(authorization)
                .authorizedScopes(authorization.getAuthorizedScopes())
                .tokenType(OAuth2TokenType.ACCESS_TOKEN)
                .build();
        OAuth2AccessToken accessToken = getAccessTokenGenerator().generate(tokenContext);

        OAuth2RefreshToken refreshToken = null;
        // 修改刷新令牌发放条件:客户端支持REFRESH_TOKEN 且 授权范围包含offline_access
        boolean shouldIssueRefreshToken = authorization.getRegisteredClient().getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)
                && authorization.getAuthorizedScopes().contains("offline_access");
        if (shouldIssueRefreshToken) {
            tokenContext = OAuth2TokenContext.builder(tokenContext)
                    .tokenType(OAuth2TokenType.REFRESH_TOKEN)
                    .build();
            refreshToken = getRefreshTokenGenerator().generate(tokenContext);
        }

        // 构造并返回认证结果(复用父类结果构造逻辑)
        return OAuth2AuthorizationCodeAuthenticationToken.withAuthenticatedPrincipal(
                authorizationCodeAuthentication.getPrincipal())
                .registeredClient(authorization.getRegisteredClient())
                .authorizationCode(authorizationCodeAuthentication.getAuthorizationCode())
                .accessToken(accessToken)
                .refreshToken(refreshToken)
                .authorizedScopes(authorization.getAuthorizedScopes())
                .build();
    }
}

2. 注册自定义Provider到Spring容器

在授权服务器配置类中,替换默认的OAuth2AuthorizationCodeAuthenticationProvider:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;

@Configuration
public class AuthorizationServerConfig {

    // 配置默认令牌生成器(如需自定义令牌逻辑可扩展)
    @Bean
    public OAuth2TokenGenerator<?> tokenGenerator() {
        OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
        OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
        return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, refreshTokenGenerator);
    }

    @Bean
    public OAuth2AuthorizationCodeAuthenticationProvider customAuthorizationCodeAuthenticationProvider(
            OAuth2TokenGenerator<?> tokenGenerator) {
        CustomOAuth2AuthorizationCodeAuthenticationProvider provider =
                new CustomOAuth2AuthorizationCodeAuthenticationProvider();
        provider.setAccessTokenGenerator((OAuth2AccessTokenGenerator) tokenGenerator);
        provider.setRefreshTokenGenerator((OAuth2RefreshTokenGenerator) tokenGenerator);
        // 按需注入其他依赖,如OAuth2AuthorizationService
        return provider;
    }
}

3. 补充客户端配置

确保客户端已开启REFRESH_TOKEN授权类型,并允许offline_access scope:

import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.UUID;

@Configuration
public class ClientConfig {

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("your-client-id")
                .clientSecret("{noop}your-client-secret")
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) // 必须保留
                .redirectUri("https://your-client-redirect-uri")
                .scope("openid")
                .scope("profile")
                .scope("offline_access") // 允许客户端请求该scope
                .build();
        return new InMemoryRegisteredClientRepository(registeredClient);
    }
}

关键说明

  • 仅修改刷新令牌发放条件,完全复用父类的核心认证、令牌生成逻辑,避免全量复制代码带来的维护成本。
  • 符合OpenID Connect规范:仅当客户端明确请求offline_access scope时,才发放用于离线访问的refresh_token。

内容的提问来源于stack exchange,提问作者nverbos-godaddy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 01:40:09