基于Spring Security 6实现Google社交登录后生成自定义Access Token
问题
我正在用Spring Security 6实现Spring授权服务器,目前已完成本地数据库注册用户的简单认证,可正常生成Access Token。
需要集成Google作为认证提供商实现社交登录,但要求Access Token仍由我的授权服务器生成:仅让Google负责登录验证,授权服务器收到Google的成功响应后,用用户名查询本地数据库获取权限等用户数据,进而生成Token返回给客户端。
未找到可参考的实现示例,恳请提供帮助。
注:已完成Google账号配置,当前可与授权服务器正常通信。
我的安全过滤器链配置如下:
@Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .oidc(oidc -> oidc .userInfoEndpoint(userInfo -> userInfo.userInfoMapper(userInfoMapper())) .logoutEndpoint(withDefaults())); http.exceptionHandling(exceptions -> exceptions .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint(FORM_LOGIN_URL))) .oauth2ResourceServer(resourceServer -> resourceServer.jwt(withDefaults())); return http.build(); } @Bean @Order(2) public SecurityFilterChain oauth2SecurityFilterChain(HttpSecurity http) throws Exception { http .oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults())) .formLogin(formLogin -> formLogin.loginPage(FORM_LOGIN_URL).permitAll()) .oauth2Login(oauth2 -> oauth2.loginPage(FORM_LOGIN_URL).permitAll()) .logout(logout -> logout .logoutUrl(LOGOUT_ENDPOINT) .logoutSuccessUrl(FORM_LOGIN_URL) .deleteCookies("JSESSIONID", "remember-me") .invalidateHttpSession(true) .clearAuthentication(true)); return http.build(); }
解决方案
1. 自定义OAuth2登录成功处理器
拦截Google登录成功后的请求,替换默认跳转逻辑,改为查询本地用户并触发授权服务器生成Token:
@Component public class OAuth2LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { private final OAuth2AuthorizationService authorizationService; private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator; private final RegisteredClientRepository registeredClientRepository; private final UserDetailsService userDetailsService; public OAuth2LoginSuccessHandler(OAuth2AuthorizationService authorizationService, OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator, RegisteredClientRepository registeredClientRepository, UserDetailsService userDetailsService) { this.authorizationService = authorizationService; this.tokenGenerator = tokenGenerator; this.registeredClientRepository = registeredClientRepository; this.userDetailsService = userDetailsService; } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { // 从Google认证信息中提取用户标识(示例用email,也可使用sub字段) OAuth2AuthenticationToken oauth2Token = (OAuth2AuthenticationToken) authentication; String userIdentifier = oauth2Token.getPrincipal().getAttribute("email"); // 查询本地数据库获取用户权限等信息 UserDetails localUser = userDetailsService.loadUserByUsername(userIdentifier); // 构建包含本地权限的认证对象 UsernamePasswordAuthenticationToken localAuth = new UsernamePasswordAuthenticationToken( localUser, null, localUser.getAuthorities()); // 解析请求中的客户端ID(实际场景需根据授权流程调整) String clientId = request.getParameter("client_id"); RegisteredClient registeredClient = registeredClientRepository.findByClientId(clientId); // 生成Access Token OAuth2TokenContext tokenContext = OAuth2TokenContext.builder() .registeredClient(registeredClient) .principal(localAuth) .tokenType(OAuth2TokenType.ACCESS_TOKEN) .build(); OAuth2Token accessToken = tokenGenerator.generate(tokenContext); // 将Token以JSON格式返回给客户端 response.setContentType(MediaType.APPLICATION_JSON_VALUE); new ObjectMapper().writeValue(response.getWriter(), Collections.singletonMap("access_token", accessToken.getTokenValue())); } }
2. 绑定自定义处理器到OAuth2Login流程
修改oauth2SecurityFilterChain配置,指定自定义的登录成功处理器:
@Bean @Order(2) public SecurityFilterChain oauth2SecurityFilterChain(HttpSecurity http, OAuth2LoginSuccessHandler successHandler) throws Exception { http .oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults())) .formLogin(formLogin -> formLogin.loginPage(FORM_LOGIN_URL).permitAll()) .oauth2Login(oauth2 -> oauth2 .loginPage(FORM_LOGIN_URL) .permitAll() .successHandler(successHandler)) // 绑定自定义处理器 .logout(logout -> logout .logoutUrl(LOGOUT_ENDPOINT) .logoutSuccessUrl(FORM_LOGIN_URL) .deleteCookies("JSESSIONID", "remember-me") .invalidateHttpSession(true) .clearAuthentication(true)); return http.build(); }
3. 关联本地用户与Google认证标识
在UserDetailsService实现中处理用户关联逻辑:
- 若通过Google标识查询到本地用户,直接返回包含本地权限的
UserDetails对象 - 若未查询到用户,可根据Google返回的信息自动创建本地用户(初始化默认权限),确保后续能正常生成包含权限的Token
4. 确保必要Bean存在
确认上下文已配置RegisteredClientRepository、OAuth2AuthorizationService、OAuth2TokenGenerator等核心Bean,Spring授权服务器默认配置已包含这些组件,如有自定义需求可自行实现扩展。
内容的提问来源于stack exchange,提问作者Andeson Morais
相关产品推荐
相关产品推荐

