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(); }
额外注意点
- 确保你的
ClientRegistration和OAuth2ProtectedResourceDetails配置完全一致(clientId、clientSecret、accessTokenUri、scope等),否则令牌无法正常使用。 OAuth2ClientContext是request-scoped的,所以在注入和使用时要确保处于web请求上下文内。
内容的提问来源于stack exchange,提问作者Hans
相关产品推荐
相关产品推荐

