Spring Security OAuth2 Client请求令牌时如何添加自定义UserId请求头
问题
我开发了一个REST应用,该应用接收客户端请求后访问资源服务器,同时采用OAuth2的client_credentials模式完成授权。但授权服务器除默认请求外,还要求请求头中携带UserId,且该值需来自客户端的请求。
当前使用的配置Bean如下:
@Configuration public class WebClientConfig { @Bean OAuth2AuthorizedClientManager authorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) { OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder() .clientCredentials() .build(); DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientRepository); authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); return authorizedClientManager; } }
请求令牌的代码:
OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("test").principal("test").build(); OAuth2AccessToken token = authorizedClientManager.authorize(oAuth2AuthorizeRequest).getAccessToken();
需要实现的效果是,向授权服务器发送获取令牌的POST请求时,添加UserId请求头,示例请求如下:
POST /oauth2/token HTTP/1.1 Host: auth-server:1111 Content-Type: application/x-www-form-urlencoded Authorization: Basic bWVzc2FnaW5nLWNsaWVudDpzZWNyZXQ= UserId : naW5nLbWVzcVudDpzZ grant_type=client_credentials&scope=message.read
解决方案
要在client_credentials模式下的令牌请求中添加自定义请求头,需要自定义ClientCredentialsTokenResponseClient,将其配置到OAuth2客户端授权提供者中,同时传递来自客户端请求的UserId值。
步骤1:自定义ClientCredentialsTokenResponseClient
创建自定义令牌响应客户端,在发送请求前注入UserId请求头:
public class CustomClientCredentialsTokenResponseClient extends DefaultClientCredentialsTokenResponseClient { @Override public OAuth2AccessTokenResponse getTokenResponse(OAuth2ClientCredentialsGrantRequest grantRequest) { // 从授权请求的额外参数中获取UserId String userId = (String) grantRequest.getAdditionalParameters().get("UserId"); // 添加请求拦截器,注入自定义头 RestTemplate restTemplate = getRestTemplate(); restTemplate.getInterceptors().add((request, body, execution) -> { if (userId != null) { request.getHeaders().add("UserId", userId); } return execution.execute(request, body); }); return super.getTokenResponse(grantRequest); } }
步骤2:配置自定义客户端到授权提供者
修改WebClientConfig,将自定义令牌客户端绑定到client_credentials授权提供者,并设置属性映射器传递自定义参数:
@Configuration public class WebClientConfig { @Bean OAuth2AuthorizedClientManager authorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) { // 实例化自定义令牌客户端 CustomClientCredentialsTokenResponseClient tokenResponseClient = new CustomClientCredentialsTokenResponseClient(); // 构建授权提供者,绑定自定义客户端 OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder() .clientCredentials(configurer -> configurer.tokenResponseClient(tokenResponseClient)) .build(); DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientRepository); authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); // 设置属性映射器,将授权请求中的属性传递到令牌请求参数 authorizedClientManager.setContextAttributesMapper(authorizeRequest -> { Map<String, Object> attributes = new HashMap<>(); attributes.putAll(authorizeRequest.getAttributes()); return attributes; }); return authorizedClientManager; } }
步骤3:请求令牌时传递UserId
从客户端请求中获取UserId(比如从请求头、参数中提取),并将其添加到OAuth2AuthorizeRequest的属性中:
// 假设从当前客户端请求的Header中获取UserId String userId = request.getHeader("UserId"); // 构建授权请求时注入UserId属性 OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("test") .principal("test") .attribute("UserId", userId) .build(); OAuth2AccessToken token = authorizedClientManager.authorize(oAuth2AuthorizeRequest).getAccessToken();
这样,当授权管理器发起令牌请求时,自定义客户端会自动将UserId注入到请求头中,满足授权服务器的校验要求。
内容的提问来源于stack exchange,提问作者javaLearnMushigi
相关产品推荐
相关产品推荐

