如何在Spring Authorization Server中支持通过offline_access scope请求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_accessscope时,才发放用于离线访问的refresh_token。
内容的提问来源于stack exchange,提问作者nverbos-godaddy
相关产品推荐
相关产品推荐

