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

Spring Boot 2.0.0.M7与Spring Cloud Finchley M5 OAuth2令牌中继问题求助

解决Spring Boot 2.0.0.M7 + Spring Cloud Finchley M5下OAuth2令牌中继的问题

Hi there, let's break down why your OAuth2RestTemplate is throwing that UserRedirectRequiredException and fix it up.

核心问题:两套OAuth体系的上下文不共享

你用的oauth2Login()是Spring Security 5.x的原生OAuth2登录功能,而OAuth2RestTemplate属于Spring Security OAuth 2.x(旧版组件,现在已被标记为废弃)。这两套组件默认使用完全独立的上下文存储,所以oauth2Login()生成的OAuth2AccessToken不会自动存入OAuth2ClientContext供OAuth2RestTemplate使用,这就是为什么调用时会触发重定向要求——它根本拿不到已有的令牌。

解决方案1:同步令牌到OAuth2ClientContext

我们可以添加一个自定义过滤器,把Spring Security 5认证成功后的令牌手动同步到OAuth2ClientContext中,让OAuth2RestTemplate能读取到它。

修改你的WebSecurityConfig:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private OAuth2ClientContext oauth2ClientContext;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .oauth2Login()
                .and()
            // 添加令牌同步过滤器
            .addFilterAfter(new TokenRelayFilter(oauth2ClientContext), OAuth2ClientContextFilter.class)
            .csrf().disable();
    }

    // 自定义过滤器:将Spring Security 5的令牌同步到OAuth2ClientContext
    private static class TokenRelayFilter extends OncePerRequestFilter {

        private final OAuth2ClientContext oauth2ClientContext;

        public TokenRelayFilter(OAuth2ClientContext oauth2ClientContext) {
            this.oauth2ClientContext = oauth2ClientContext;
        }

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            // 从Spring Security 5的认证对象中提取令牌
            if (auth instanceof OAuth2AuthenticationToken) {
                OAuth2AuthenticationToken oauth2Token = (OAuth2AuthenticationToken) auth;
                OAuth2AccessToken accessToken = oauth2Token.getAuthorizedClient().getAccessToken();
                // 存入OAuth2ClientContext供OAuth2RestTemplate使用
                oauth2ClientContext.setAccessToken(accessToken);
            }
            filterChain.doFilter(request, response);
        }
    }
}

解决方案2:迁移到Spring Security 5原生的WebClient(推荐)

因为OAuth2RestTemplate已经被官方废弃,更推荐使用Spring Security 5原生支持的WebClient,它能自动处理令牌中继,不需要手动维护上下文:

添加WebClient的配置类:

@Configuration
public class OAuth2WebClientConfig {

    @Bean
    public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        // 创建OAuth2客户端过滤器
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Filter =
                new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        // 设置默认使用的客户端注册ID(对应你的"myauth")
        oauth2Filter.setDefaultClientRegistrationId("myauth");
        // 构建WebClient
        return WebClient.builder()
                .apply(oauth2Filter.oauth2Configuration())
                .build();
    }

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepo,
            OAuth2AuthorizedClientService clientService) {
        // 配置授权客户端提供者(支持授权码、刷新令牌)
        OAuth2AuthorizedClientProvider provider = OAuth2AuthorizedClientProviderBuilder.builder()
                .authorizationCode()
                .refreshToken()
                .build();
        DefaultOAuth2AuthorizedClientManager manager =
                new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepo, clientService);
        manager.setAuthorizedClientProvider(provider);
        return manager;
    }
}

之后你就可以直接注入WebClient来发起携带令牌的请求,无需手动处理令牌存储:

@Autowired
private WebClient webClient;

public String callProtectedApi() {
    return webClient.get()
            .uri("https://your-protected-api-url")
            .retrieve()
            .bodyToMono(String.class)
            .block();
}

额外注意点

  1. 确保你的ClientRegistration和OAuth2ProtectedResourceDetails配置完全一致(clientId、clientSecret、accessTokenUri、scope等),否则令牌无法正常使用。
  2. OAuth2ClientContext是request-scoped的,所以在注入和使用时要确保处于web请求上下文内。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:38:35