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

如何在Spring中为每个用户生成新Token进行WebClient请求认证?

基于Subject隔离的Token Exchange认证实现方案

针对你的需求——为每个requested_subject生成独立Token且避免复用,方案2(为每个Subject提供专属的ReactiveOAuth2AuthorizedClientService)确实是最贴合Spring生态的简洁实现方式,具体落地步骤如下:

1. 实现Subject专属的AuthorizedClientService工厂

创建一个工厂类,为每个不同的requested_subject生成独立的授权客户端服务实例,用线程安全的Map缓存实例,避免重复创建:

@Component
class SubjectAuthorizedClientServiceFactory(
    private val clientRegistrationRepository: ReactiveClientRegistrationRepository
) {
    // 用ConcurrentHashMap保证反应式环境下的线程安全
    private val clientServiceCache = ConcurrentHashMap<String, ReactiveOAuth2AuthorizedClientService>()

    fun getOrCreate(subject: String): ReactiveOAuth2AuthorizedClientService {
        // 原子性获取或创建专属的授权客户端服务
        return clientServiceCache.computeIfAbsent(subject) {
            InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository)
        }
    }
}

2. 修改WebClient过滤器逻辑

在原有的WebClient构建代码中,通过工厂获取当前Subject对应的ReactiveOAuth2AuthorizedClientService,确保每个Subject的Token缓存完全隔离:

@Autowired
private lateinit var subjectClientServiceFactory: SubjectAuthorizedClientServiceFactory

// ...

MonoGraphQLClient.createWithWebClient(WebClient.builder()
    .baseUrl(properties.httpUrl)
    .filter { request, next ->
        val requestedSubject = request.attribute(REQUESTING_USER_ATTRIBUTE)
            .map(Any::toString)
            .orElseThrow { IllegalArgumentException("Missing requesting user attribute") }
        
        // 获取当前Subject专属的授权客户端服务
        val subjectClientService = subjectClientServiceFactory.getOrCreate(requestedSubject)
        
        val authorizedClientManager = AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
            clientRegistrationRepository,
            subjectClientService
        ).apply {
            setAuthorizedClientProvider(TokenExchangeReactiveOAuth2AuthorizedClientProvider().apply {
                setSubjectTokenResolver { Mono.just(OAuth2Token { null }) }
                setAccessTokenResponseClient(LenientWebClientReactiveTokenExchangeTokenResponseClient().apply {
                    parametersProvider = {
                        LinkedMultiValueMap<String, String>().apply {
                            add("audience", properties.tokenAudience)
                            add("requested_subject", requestedSubject)
                        }
                    }
                })
            })
        }

        ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager).also {
            it.setDefaultClientRegistrationId(clientRegistrationRepository.first().registrationId)
        }.filter(request, next)
    }
    .build()

方案优势说明

  • Spring生态兼容:完全复用官方InMemoryReactiveOAuth2AuthorizedClientService实现,无需自定义复杂缓存逻辑
  • 线程安全:ConcurrentHashMap的computeIfAbsent方法保证了反应式环境下实例创建的原子性,避免并发问题
  • Token隔离:每个requested_subject拥有独立的Token缓存,不同用户的Token不会互相复用,过期后也会独立刷新
  • 扩展性强:如果后续需要分布式部署,只需将InMemoryReactiveOAuth2AuthorizedClientService替换为Redis等分布式缓存实现即可,工厂类逻辑无需大幅修改

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 13:27:04