Spring Authorization Server中如何在client_credentials授权类型下扩展Token端点接收额外参数并生成关联的AccessToken?
你选的方向完全没错!实现AuthenticationConverter和OAuth2TokenGenerator<OAuth2AccessToken>正是在Spring Authorization Server中扩展client_credentials授权、处理额外参数并生成关联Token的核心路径。下面我会一步步帮你把整个流程补全,确保能实现你想要的效果:
1. 完善自定义AuthenticationConverter,提取并传递额外参数
首先要从Token端点的请求中提取额外参数,并把它注入到认证流程的Authentication对象中,这样后续的Token生成逻辑就能拿到这个参数。我们可以基于默认的OAuth2ClientCredentialsAuthenticationConverter做扩展:
import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationConverter; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; public class CustomClientCredentialsAuthenticationConverter implements AuthenticationConverter { // 复用默认转换器处理基础参数(grant_type、client_id、client_secret等) private final OAuth2ClientCredentialsAuthenticationConverter delegate = new OAuth2ClientCredentialsAuthenticationConverter(); @Override public Authentication convert(HttpServletRequest request) { Authentication authentication = delegate.convert(request); if (authentication instanceof OAuth2ClientCredentialsAuthenticationToken clientCredentialsToken) { // 提取你的额外参数,这里假设参数名为"custom_param" String customParam = request.getParameter("custom_param"); // 可选:验证参数是否必填 if (!StringUtils.hasText(customParam)) { throw new OAuth2AuthenticationException( new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, "缺少必填参数:custom_param", null) ); } // 将额外参数存入Authentication的details字段,供后续流程使用 Map<String, Object> updatedDetails = new HashMap<>(clientCredentialsToken.getDetails()); updatedDetails.put("custom_param", customParam); // 返回包含额外参数的新认证Token return new OAuth2ClientCredentialsAuthenticationToken( clientCredentialsToken.getClientPrincipal(), updatedDetails, clientCredentialsToken.getAuthorities() ); } return authentication; } }
2. 实现自定义TokenGenerator,关联额外参数生成AccessToken
接下来要在Token生成阶段,把额外参数关联到AccessToken中——可以把参数存入Token的Claims里,也可以基于参数生成唯一的Token(确保同一客户端不同参数生成不同Token):
import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2AccessTokenGenerator; import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext; import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken; import org.springframework.util.StringUtils; public class CustomOAuth2AccessTokenGenerator implements OAuth2TokenGenerator<OAuth2AccessToken> { private final DefaultOAuth2AccessTokenGenerator delegate = new DefaultOAuth2AccessTokenGenerator(); @Override public OAuth2AccessToken generate(OAuth2TokenContext context) { // 从认证Token中取出之前存入的额外参数 if (context.getAuthentication() instanceof OAuth2ClientCredentialsAuthenticationToken clientCredentialsToken) { String customParam = (String) clientCredentialsToken.getDetails().get("custom_param"); if (StringUtils.hasText(customParam)) { // 将额外参数写入Token的Claims,方便后续接口或资源服务器获取 context.getClaims().claim("custom_param", customParam); // 如果你需要让Token与client_id+custom_param绑定(同一客户端不同参数生成不同Token) // 由于Spring Authorization Server默认会为每个不同的认证请求生成独立的OAuth2Authorization记录 // 只要参数不同,就会生成不同的授权记录,自然会生成不同的Token,这一步通常无需额外操作 } } // 调用默认生成器完成Token的最终生成 return delegate.generate(context); } @Override public boolean supports(OAuth2TokenContext context) { // 仅支持AccessToken的生成 return delegate.supports(context); } }
3. 配置自定义组件到授权服务器流程
最后要把上面两个自定义组件注册到Spring Authorization Server的配置中,替换默认的转换器和生成器:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration; import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer; @Configuration @EnableWebSecurity public class AuthorizationServerConfig { @Bean @Order(1) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { // 应用授权服务器默认安全配置 OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .tokenEndpoint(tokenEndpoint -> tokenEndpoint // 替换client_credentials授权的请求转换器 .accessTokenRequestConverter(new CustomClientCredentialsAuthenticationConverter()) // 配置自定义的AccessToken生成器 .accessTokenGenerator(new CustomOAuth2AccessTokenGenerator()) ); return http.build(); } // 请根据你的需求添加其他必要的Bean,比如RegisteredClientRepository、ClientRegistrationRepository等 // ... }
测试验证
完成配置后,你可以用Postman或curl调用Token端点,示例请求:
POST /oauth2/token Content-Type: application/x-www-form-urlencoded grant_type=client_credentials&client_id=your-client-id&client_secret=your-client-secret&custom_param=your-custom-value
你可以通过解析返回的AccessToken(用JWT解析工具)查看Claims中是否包含custom_param字段,同时测试不同的custom_param值是否会生成不同的AccessToken。
备注:内容来源于stack exchange,提问作者hugoalexandremf
相关产品推荐
相关产品推荐

