如何为Spring Security社交登录授权URI添加自定义查询参数
给Spring Security OAuth2授权URI追加自定义查询参数
问题背景
当前使用Spring Security 5.2.12.RELEASE实现谷歌、脸书等社交平台的OAuth2登录,登录流程为调用端点https://example.com/oauth2/authorize/{registrationId},会话信息存入Cookie后重定向到服务商授权URI。
现有核心配置如下:
SecurityConfig.java
package com.example.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.example.security.*; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity( securedEnabled = true, jsr250Enabled = true, prePostEnabled = true ) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomOAuth2UserService customOAuth2UserService; @Autowired private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler; @Autowired private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler; @Autowired private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository; @Bean public TokenAuthenticationFilter tokenAuthenticationFilter() { return new TokenAuthenticationFilter(); } /* By default, Spring OAuth2 uses HttpSessionOAuth2AuthorizationRequestRepository to save the authorization request. But, since our service is stateless, we can't save it in the session. We'll save the request in a Base64 encoded cookie instead. */ @Bean public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() { return new HttpCookieOAuth2AuthorizationRequestRepository(); } @Override protected void configure(HttpSecurity http) throws Exception { http .cors() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .csrf() .disable() .formLogin() .disable() .httpBasic() .disable() .exceptionHandling() .authenticationEntryPoint(new RestAuthenticationEntryPoint()) .and() .authorizeRequests() .antMatchers("/oauth2/**") .permitAll() .anyRequest() .authenticated() .and() .oauth2Login() .authorizationEndpoint() .baseUri("/oauth2/authorize") .authorizationRequestRepository(cookieAuthorizationRequestRepository()) .and() .redirectionEndpoint() .baseUri("/oauth2/callback/*") .and() .userInfoEndpoint() .userService(customOAuth2UserService) .and() .successHandler(oAuth2AuthenticationSuccessHandler) .failureHandler(oAuth2AuthenticationFailureHandler) ; // Add our custom Token based authentication filter http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } public HttpCookieOAuth2AuthorizationRequestRepository getHttpCookieOAuth2AuthorizationRequestRepository() { return httpCookieOAuth2AuthorizationRequestRepository; } public void setHttpCookieOAuth2AuthorizationRequestRepository( HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository) { this.httpCookieOAuth2AuthorizationRequestRepository = httpCookieOAuth2AuthorizationRequestRepository; } }
application.properties(谷歌OAuth2配置)
#Social Providers OAuth2 settings spring.security.oauth2.client.registration.google.clientId=abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com spring.security.oauth2.client.registration.google.clientSecret=secret spring.security.oauth2.client.registration.google.redirectUri=https://example.com/oauth2/callback/{registrationId} spring.security.oauth2.client.registration.google.scope=email,profile
当前生成的谷歌授权URI示例:
https://accounts.google.com/o/oauth2/v2/auth/oauthchooseaccount ?response_type=code &client_id=abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com &scope=email%20profile &state=zyxwvutsrqponmlkjihgfedcba &redirect_uri=https%3A%2F%2Fexample.com%2Foauth2%2Fcallback%2Fgoogle &service=lso &o2v=2 &flowName=GeneralOAuthFlow
需要给该URI追加查询参数&prompt=select_account,但直接在application.properties中添加spring.security.oauth2.client.registration.google.prompt=select_account无效,官方文档未提供该配置项。
解决方案
通过自定义OAuth2AuthorizationRequestResolver来修改授权请求的额外参数,具体步骤如下:
1. 实现自定义的OAuth2AuthorizationRequestResolver
import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver; import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { private final OAuth2AuthorizationRequestResolver delegate; public CustomOAuth2AuthorizationRequestResolver(OAuth2AuthorizationRequestResolver delegate) { this.delegate = delegate; } @Override public OAuth2AuthorizationRequest resolve(HttpServletRequest request) { OAuth2AuthorizationRequest authorizationRequest = this.delegate.resolve(request); return customizeAuthorizationRequest(authorizationRequest); } @Override public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) { OAuth2AuthorizationRequest authorizationRequest = this.delegate.resolve(request, clientRegistrationId); return customizeAuthorizationRequest(authorizationRequest); } private OAuth2AuthorizationRequest customizeAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest) { if (authorizationRequest == null) { return null; } // 仅针对谷歌客户端追加prompt参数,其他平台可按需扩展 ClientRegistration clientRegistration = authorizationRequest.getClientRegistration(); if ("google".equals(clientRegistration.getRegistrationId())) { Map<String, Object> additionalParams = new HashMap<>(authorizationRequest.getAdditionalParameters()); additionalParams.put("prompt", "select_account"); return OAuth2AuthorizationRequest.from(authorizationRequest) .additionalParameters(additionalParams) .build(); } return authorizationRequest; } }
2. 在SecurityConfig中配置自定义Resolver
首先注入ClientRegistrationRepository,然后修改configure(HttpSecurity http)方法中的oauth2Login配置:
// 在SecurityConfig类中添加注入 @Autowired private ClientRegistrationRepository clientRegistrationRepository; @Override protected void configure(HttpSecurity http) throws Exception { // 创建默认授权请求解析器,并用自定义解析器包装 OAuth2AuthorizationRequestResolver defaultResolver = new DefaultOAuth2AuthorizationRequestResolver( clientRegistrationRepository, "/oauth2/authorize"); OAuth2AuthorizationRequestResolver customResolver = new CustomOAuth2AuthorizationRequestResolver(defaultResolver); http .cors() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .csrf() .disable() .formLogin() .disable() .httpBasic() .disable() .exceptionHandling() .authenticationEntryPoint(new RestAuthenticationEntryPoint()) .and() .authorizeRequests() .antMatchers("/oauth2/**") .permitAll() .anyRequest() .authenticated() .and() .oauth2Login() .authorizationEndpoint() .baseUri("/oauth2/authorize") .authorizationRequestRepository(cookieAuthorizationRequestRepository()) // 配置自定义授权请求解析器 .authorizationRequestResolver(customResolver) .and() .redirectionEndpoint() .baseUri("/oauth2/callback/*") .and() .userInfoEndpoint() .userService(customOAuth2UserService) .and() .successHandler(oAuth2AuthenticationSuccessHandler) .failureHandler(oAuth2AuthenticationFailureHandler); http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); }
原理说明
Spring Security的DefaultOAuth2AuthorizationRequestResolver负责构建OAuth2授权请求的URI,我们通过包装该解析器,在授权请求构建完成后追加自定义参数,最终生成的授权URI会包含prompt=select_account参数。
内容的提问来源于stack exchange,提问作者nick_j_white
相关产品推荐
相关产品推荐

