如何在OAuth2令牌请求中添加自定义参数生成带自定义声明的令牌?
在Spring Security OAuth2中为Client Credentials令牌请求添加自定义参数
Q1 解答
Spring Security OAuth2的组件按职责分层设计:
OAuth2AuthorizeRequest是授权流程的入口请求对象,承载触发授权的上下文信息(客户端ID、用户主体、自定义属性等),面向顶层的OAuth2AuthorizedClientManager。OAuth2ClientCredentialsGrantRequest是Client Credentials授权类型的专用请求对象,仅聚焦该授权类型所需的核心标准化信息(客户端注册信息、授权类型),是底层TokenResponseClient处理令牌请求的输入。
两者属于流程的不同阶段,框架默认没有在OAuth2ClientCredentialsGrantRequest中保留OAuth2AuthorizeRequest的引用或属性——因为GrantRequest的设计目标是标准化授权类型的请求参数,而非传递上层上下文的自定义数据。
基于框架的解决方案
方案一:扩展GrantRequest传递自定义参数(通用场景)
通过扩展OAuth2ClientCredentialsGrantRequest来携带自定义参数,配合自定义的AuthorizedClientProvider和请求转换器完成参数传递。
- 自定义GrantRequest类
public class CustomClientCredentialsGrantRequest extends OAuth2ClientCredentialsGrantRequest { private final Map<String, String> customParameters; public CustomClientCredentialsGrantRequest(OAuth2ClientCredentialsGrantRequest originalRequest, Map<String, String> customParameters) { super(originalRequest.getClientRegistration()); this.customParameters = customParameters; } public Map<String, String> getCustomParameters() { return customParameters; } }
- 自定义ClientCredentials授权提供者
重写authorize方法,从OAuth2AuthorizationContext中提取OAuth2AuthorizeRequest的自定义属性,创建自定义GrantRequest实例:
public class CustomClientCredentialsOAuth2AuthorizedClientProvider extends ClientCredentialsOAuth2AuthorizedClientProvider { @Override public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) { // 从授权请求中提取自定义属性 Map<String, String> customParams = context.getAuthorizeRequest().getAttributes().entrySet().stream() .filter(entry -> entry.getKey().startsWith("custom_")) // 自定义属性前缀标识 .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toString())); // 创建自定义GrantRequest OAuth2ClientCredentialsGrantRequest originalGrantRequest = new OAuth2ClientCredentialsGrantRequest(context.getClientRegistration()); CustomClientCredentialsGrantRequest customGrantRequest = new CustomClientCredentialsGrantRequest(originalGrantRequest, customParams); // 配置支持自定义GrantRequest的转换器 DefaultClientCredentialsTokenResponseClient tokenResponseClient = (DefaultClientCredentialsTokenResponseClient) getTokenResponseClient(); tokenResponseClient.setRequestEntityConverter(new CustomOAuth2ClientCredentialsGrantRequestEntityConverter()); try { OAuth2TokenResponse tokenResponse = tokenResponseClient.getTokenResponse(customGrantRequest); return new OAuth2AuthorizedClient( context.getClientRegistration(), context.getPrincipalName(), tokenResponse.getAccessToken(), tokenResponse.getRefreshToken() ); } catch (OAuth2AuthorizationException ex) { throw ex; } } }
- 自定义请求实体转换器
重写createParameters方法,读取自定义GrantRequest中的参数并添加到令牌请求体:
public class CustomOAuth2ClientCredentialsGrantRequestEntityConverter extends OAuth2ClientCredentialsGrantRequestEntityConverter { @Override protected MultiValueMap<String, String> createParameters(OAuth2ClientCredentialsGrantRequest grantRequest) { MultiValueMap<String, String> parameters = super.createParameters(grantRequest); // 读取自定义参数并添加到请求体 if (grantRequest instanceof CustomClientCredentialsGrantRequest customRequest) { customRequest.getCustomParameters().forEach(parameters::add); } return parameters; } }
- 配置OAuth2AuthorizedClientManager
@Bean public OAuth2AuthorizedClientManager authorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientService authorizedClientService) { CustomClientCredentialsOAuth2AuthorizedClientProvider clientCredentialsProvider = new CustomClientCredentialsOAuth2AuthorizedClientProvider(); DefaultOAuth2AuthorizedClientManager manager = new DefaultOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientService ); manager.setAuthorizedClientProvider(clientCredentialsProvider); return manager; }
- 调用示例
// 构建授权请求时添加自定义属性 OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("my-client") .attribute("custom_parameter1", "user-specific-value") .attribute("custom_parameter2", "another-value") .build(); OAuth2AuthorizedClient authorizedClient = oauthClientManager.authorize(authorizeRequest);
方案二:利用RequestAttribute传递参数(Web环境专属)
如果是Web应用,可以通过RequestContextHolder在请求上下文中传递自定义参数,适合不需要跨线程传递的场景。
- 自定义请求实体转换器
public class CustomOAuth2ClientCredentialsGrantRequestEntityConverter extends OAuth2ClientCredentialsGrantRequestEntityConverter { @Override protected MultiValueMap<String, String> createParameters(OAuth2ClientCredentialsGrantRequest grantRequest) { MultiValueMap<String, String> parameters = super.createParameters(grantRequest); // 从当前请求上下文获取自定义参数 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); if (requestAttributes != null) { String customParam1 = (String) requestAttributes.getAttribute("custom_parameter1", RequestAttributes.SCOPE_REQUEST); if (customParam1 != null) { parameters.add("custom_parameter1", customParam1); } } return parameters; } }
- 配置TokenResponseClient
@Bean public OAuth2AuthorizedClientManager authorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientService authorizedClientService) { ClientCredentialsOAuth2AuthorizedClientProvider clientCredentialsProvider = new ClientCredentialsOAuth2AuthorizedClientProvider(); // 配置自定义转换器 DefaultClientCredentialsTokenResponseClient tokenResponseClient = new DefaultClientCredentialsTokenResponseClient(); tokenResponseClient.setRequestEntityConverter(new CustomOAuth2ClientCredentialsGrantRequestEntityConverter()); clientCredentialsProvider.setTokenResponseClient(tokenResponseClient); DefaultOAuth2AuthorizedClientManager manager = new DefaultOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientService ); manager.setAuthorizedClientProvider(clientCredentialsProvider); return manager; }
- 调用示例
// 先将自定义参数放入当前请求上下文 RequestContextHolder.getRequestAttributes().setAttribute( "custom_parameter1", "web-request-value", RequestAttributes.SCOPE_REQUEST ); // 构建并执行授权请求 OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("my-client").build(); OAuth2AuthorizedClient authorizedClient = oauthClientManager.authorize(authorizeRequest);
内容的提问来源于stack exchange,提问作者Ryan
相关产品推荐
相关产品推荐

