You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 07:55:02