Spring Security OAuth2登录成功处理器中OAuth2AuthorizationRequest为空问题排查
问题原因
Spring Security在OAuth2登录的默认流程中,OAuth2LoginAuthenticationProvider完成认证逻辑后,会调用你配置的HttpSessionOAuth2AuthorizationRequestRepository的removeAuthorizationRequest方法,提前清除会话中的授权请求对象。等你的自定义AuthenticationSuccessHandler执行时,会话里已经没有这个对象了,所以调用removeAuthorizationRequest会返回null。
解决方案一:自定义认证Provider,提前保存自定义参数
创建自定义的OAuth2LoginAuthenticationProvider,在默认认证逻辑执行前,把授权请求中的自定义参数存入Authentication对象的details中,这样在successHandler里就能直接获取:
1. 自定义认证Provider
public class CustomOAuth2LoginAuthenticationProvider extends OAuth2LoginAuthenticationProvider { private final OAuth2AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository; public CustomOAuth2LoginAuthenticationProvider(OAuth2AuthorizationService authorizationService, OAuth2AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) { super(authorizationService); this.authorizationRequestRepository = authorizationRequestRepository; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { OAuth2LoginAuthenticationToken authToken = (OAuth2LoginAuthenticationToken) authentication; ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (requestAttributes == null) { return super.authenticate(authentication); } HttpServletRequest request = requestAttributes.getRequest(); // 在默认逻辑移除前获取授权请求 OAuth2AuthorizationRequest authRequest = this.authorizationRequestRepository.loadAuthorizationRequest(request); if (authRequest != null && authRequest.getAdditionalParameters() != null) { // 将自定义参数存入Authentication的details authToken.setDetails(authRequest.getAdditionalParameters()); } // 执行默认认证流程(默认流程会移除授权请求) return super.authenticate(authentication); } }
2. 注册自定义Provider到Security配置
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public OAuth2LoginAuthenticationProvider customOAuth2LoginAuthenticationProvider( OAuth2AuthorizationService authorizationService, OAuth2AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) { return new CustomOAuth2LoginAuthenticationProvider(authorizationService, authorizationRequestRepository); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customOAuth2LoginAuthenticationProvider(authorizationService(), authorizationRequestRepository())); } // 其他配置(authorizationRequestRepository、customAuthorizationRequestResolver等) @Bean public OAuth2AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() { return new HttpSessionOAuth2AuthorizationRequestRepository(); } // ... 其他Bean配置 }
3. 在SuccessHandler中获取参数
@Component public class OAuth2SuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { OAuth2LoginAuthenticationToken oauthToken = (OAuth2LoginAuthenticationToken) authentication; Map<String, Object> additionalParams = (Map<String, Object>) oauthToken.getDetails(); String redirectOrigin = (String) additionalParams.get("redirect_origin"); // 处理跳转或返回逻辑 response.sendRedirect(redirectOrigin + "/login-success"); } }
解决方案二:直接将自定义参数存入HttpSession
在自定义的AuthorizationRequestResolver中,提取自定义参数后直接存入HttpSession,跳过依赖OAuth2AuthorizationRequestRepository的步骤:
1. 修改自定义AuthorizationRequestResolver
public class CustomAuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { private final OAuth2AuthorizationRequestResolver delegate; public CustomAuthorizationRequestResolver(OAuth2AuthorizationRequestResolver delegate) { this.delegate = delegate; } @Override public OAuth2AuthorizationRequest resolve(HttpServletRequest request) { OAuth2AuthorizationRequest authRequest = this.delegate.resolve(request); if (authRequest != null) { String redirectOrigin = request.getParameter("redirect_origin"); if (redirectOrigin != null) { // 直接存入session request.getSession().setAttribute("redirect_origin", redirectOrigin); // 可选:同时添加到授权请求的附加参数中 authRequest = OAuth2AuthorizationRequest.from(authRequest) .additionalParameters(Map.of("redirect_origin", redirectOrigin)) .build(); } } return authRequest; } @Override public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) { OAuth2AuthorizationRequest authRequest = this.delegate.resolve(request, clientRegistrationId); if (authRequest != null) { String redirectOrigin = request.getParameter("redirect_origin"); if (redirectOrigin != null) { request.getSession().setAttribute("redirect_origin", redirectOrigin); authRequest = OAuth2AuthorizationRequest.from(authRequest) .additionalParameters(Map.of("redirect_origin", redirectOrigin)) .build(); } } return authRequest; } }
2. 在SuccessHandler中获取参数
@Component public class OAuth2SuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { String redirectOrigin = (String) request.getSession().getAttribute("redirect_origin"); // 用完后清理session request.getSession().removeAttribute("redirect_origin"); // 处理跳转逻辑 response.sendRedirect(redirectOrigin + "/login-success"); } }
内容的提问来源于stack exchange,提问作者Roeland Van Heddegem
相关产品推荐
相关产品推荐

