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

Spring Security OAuth2客户端高并发下令牌串号问题求助

OAuth2密码模式下并发场景令牌串用问题排查与解决

问题原因

核心问题出在单例Bean的全局属性篡改与请求上下文隔离缺失:

  1. DefaultOAuth2AuthorizedClientManager是Spring容器中的单例Bean,createWebClient方法里直接调用authorizedClientManager.setContextAttributesMapper()会替换全局的属性映射器。并发场景下,多个线程同时修改该Bean的属性,后执行的用户信息会覆盖先执行的,最终导致令牌与用户身份不匹配。
  2. 所有用户的WebClient复用同一个全局authorizedClientManager,没有为每个用户的请求单独隔离凭证信息,令牌的存储与获取逻辑混淆了不同用户的身份。

解决方案

1. 避免修改全局DefaultOAuth2AuthorizedClientManager属性

不要直接修改单例Bean的全局配置,改为在WebClient的请求上下文里单独传递用户凭证。

2. 调整WebClient创建逻辑,隔离用户凭证

修改createWebClient方法,通过ServletOAuth2AuthorizedClientExchangeFilterFunction的defaultRequest为每个WebClient实例绑定专属用户属性,替代全局修改:

private WebClient createWebClient(String userLoginName, String userPassword, String clientRegistrationId) {
    ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = 
        new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
    oauth2.setDefaultClientRegistrationId(clientRegistrationId);

    // 为当前WebClient实例绑定专属用户凭证,避免全局污染
    oauth2.defaultRequest(request -> request.attributes(attrs -> {
        attrs.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, userLoginName);
        attrs.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, userPassword);
    }));

    final int size = 16 * 1024 * 1024;
    final ExchangeStrategies strategies = ExchangeStrategies.builder()
            .codecs(codecs -> codecs.defaultCodecs().maxInMemorySize(size)).build();

    return WebClient.builder()
            .filter(errorHandler())
            .exchangeStrategies(strategies)
            .baseUrl(apiBaseUrl)
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .filter(oauth2)
            .build();
}

3. 优化令牌存储的隔离性

当前配置的HttpSessionOAuth2AuthorizedClientRepository基于会话存储令牌,多用户并发场景下是安全的,但需确保每个请求的会话独立。如果是无状态场景,可替换为InMemoryOAuth2AuthorizedClientRepository,或实现自定义存储逻辑,按用户ID区分令牌。

4. (可选)使用AuthorizedClientServiceOAuth2AuthorizedClientManager强化隔离

若需更严格的令牌管理,可替换为AuthorizedClientServiceOAuth2AuthorizedClientManager,它基于OAuth2AuthorizedClientService按用户+客户端ID维度存储令牌,彻底避免串用:

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
        ClientRegistrationRepository clientRegistrationRepository,
        OAuth2AuthorizedClientService authorizedClientService) {

    OAuth2AuthorizedClientProvider authorizedClientProvider =
            OAuth2AuthorizedClientProviderBuilder.builder()
                    .password()
                    .build();

    AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
            new AuthorizedClientServiceOAuth2AuthorizedClientManager(
                    clientRegistrationRepository, authorizedClientService);
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

    return authorizedClientManager;
}

内容的提问来源于stack exchange,提问作者sahmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 11:27:24