如何在新版Spring Authorization Server中添加自定义Token Granter
一、替换/自定义Token Granter(替代旧版AbstractTokenGranter)
新版Spring Authorization Server不再沿用旧版AbstractTokenGranter的继承模式,而是通过**自定义AuthenticationProvider**来实现自定义授权类型的处理,步骤如下:
1. 定义自定义授权类型与认证Token
先声明你的自定义授权类型(比如sso_grant),并创建对应的认证Token类,用来承载请求参数:
public class SsoOAuth2AuthorizationGrantAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken { // 自定义授权类型标识 public static final String GRANT_TYPE = "sso_grant"; public SsoOAuth2AuthorizationGrantAuthenticationToken( ClientRegistration clientRegistration, Authentication clientPrincipal, Map<String, Object> additionalParameters) { super(new OAuth2AuthorizationGrant(GRANT_TYPE), clientPrincipal, additionalParameters); } }
2. 实现自定义AuthenticationProvider
这是核心逻辑,在这里你可以调用外部服务、处理用户认证、生成令牌:
@Component public class SsoOAuth2TokenGrantAuthenticationProvider implements AuthenticationProvider { private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator; private final OAuth2AuthorizationService authorizationService; // 注入你的用户服务(用来获取用户信息) private final UserService userService; // 构造方法注入依赖 public SsoOAuth2TokenGrantAuthenticationProvider( OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator, OAuth2AuthorizationService authorizationService, UserService userService) { this.tokenGenerator = tokenGenerator; this.authorizationService = authorizationService; this.userService = userService; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { SsoOAuth2AuthorizationGrantAuthenticationToken authToken = (SsoOAuth2AuthorizationGrantAuthenticationToken) authentication; // 1. 从请求参数中提取用户标识(比如user_id) String userId = (String) authToken.getAdditionalParameters().get("user_id"); // 2. 调用外部服务获取用户信息(角色、姓名、邮箱等) UserInfo userInfo = userService.getUserInfoById(userId); // 3. 构建用户认证主体,包含权限信息 Collection<GrantedAuthority> authorities = userInfo.getRoles().stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); Authentication userAuthentication = new UsernamePasswordAuthenticationToken( userInfo, null, authorities); // 4. 创建授权信息构建器 OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(authToken.getRegisteredClient()) .principalName(userAuthentication.getName()) .authorizationGrantType(new OAuth2AuthorizationGrant(GRANT_TYPE)) .attribute(OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME, authToken.getRegisteredClient().getScopes()); // 5. 生成Access Token(如果需要Refresh Token,后续会自动处理) OAuth2TokenContext tokenContext = OAuth2TokenContext.builder() .registeredClient(authToken.getRegisteredClient()) .principal(userAuthentication) .authorization(authorizationBuilder.build()) .tokenType(OAuth2TokenType.ACCESS_TOKEN) .build(); OAuth2AccessToken accessToken = (OAuth2AccessToken) this.tokenGenerator.generate(tokenContext); // 6. 存储授权信息并返回认证结果 OAuth2Authorization authorization = authorizationBuilder .token(accessToken, metadata -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, accessToken.getClaims())) .build(); this.authorizationService.save(authorization); return new OAuth2AccessTokenAuthenticationToken( authToken.getRegisteredClient(), userAuthentication, accessToken); } @Override public boolean supports(Class<?> authentication) { return SsoOAuth2AuthorizationGrantAuthenticationToken.class.isAssignableFrom(authentication); } }
3. 注册自定义Provider到授权服务器
在授权服务器配置类中,将自定义的AuthenticationProvider注册进去:
@Configuration public class AuthorizationServerConfig { // 基础授权服务器安全过滤链 @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); return http.formLogin(Customizer.withDefaults()).build(); } // 客户端配置(示例用内存存储,生产建议用数据库) @Bean public RegisteredClientRepository registeredClientRepository() { RegisteredClient ssoClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("sso-client") .clientSecret("{noop}sso-secret") // 生产用加密密码 .authorizationGrantType(new AuthorizationGrantType(SsoOAuth2AuthorizationGrantAuthenticationToken.GRANT_TYPE)) .scope("user_info") .build(); return new InMemoryRegisteredClientRepository(ssoClient); } // 注册自定义AuthenticationProvider @Bean public OAuth2AuthorizationServerConfigurer authorizationServerConfigurer( SsoOAuth2TokenGrantAuthenticationProvider ssoProvider) { return new OAuth2AuthorizationServerConfigurer() .authenticationProvider(ssoProvider); } }
二、向JWT添加自定义声明与权限
新版推荐使用OAuth2TokenCustomizer来扩展JWT声明,无需修改令牌生成器核心逻辑,直接在令牌编码阶段注入自定义信息:
@Bean public OAuth2TokenCustomizer<JwtEncodingContext> jwtTokenCustomizer() { return context -> { // 只处理Access Token(如果需要处理ID Token,可单独判断) if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { Authentication principal = context.getPrincipal(); UserInfo userInfo = (UserInfo) principal.getPrincipal(); // 添加自定义用户信息声明 context.getClaims() .claim("real_name", userInfo.getRealName()) .claim("email", userInfo.getEmail()) .claim("roles", userInfo.getRoles()); // 添加权限列表声明 Collection<String> authorities = principal.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.toList()); context.getClaims().claim("authorities", authorities); } }; }
这个自定义器会被Spring Authorization Server自动识别,在生成JWT时自动调用,将自定义声明写入令牌。
关键说明
- 旧版的
AbstractTokenGranter逻辑被拆分到了AuthenticationProvider和OAuth2TokenGenerator中,自定义授权类型的核心是实现AuthenticationProvider处理认证请求。 - 自定义JWT声明优先用
OAuth2TokenCustomizer,它是官方推荐的扩展方式,侵入性更低。
内容的提问来源于stack exchange,提问作者Mom0
相关产品推荐
相关产品推荐

