Spring Boot中Google OAuth2登录后返回/携带AuthResponse的实现
问题描述
我在Spring Boot应用中使用Google OAuth2认证,希望登录成功后返回AuthResponse对象。当前登录成功后会将用户重定向到指定URL,但有两个需求:
- 无需重定向,直接返回包含用户ID、用户名、头像URL及JWT的用户信息;
- 若仍使用重定向,如何在重定向过程中传递
AuthResponse信息,而非直接返回JSON格式的该对象?
现有代码
自定义OAuth2成功处理器
@Component public class CustomOAuth2SuccessHandler implements AuthenticationSuccessHandler { private final UserService userService; private final JwtService jwtService; private final RefreshTokenService refreshTokenService; public CustomOAuth2SuccessHandler(UserService userService, JwtService jwtService, RefreshTokenService refreshTokenService) { this.userService = userService; this.jwtService = jwtService; this.refreshTokenService = refreshTokenService; } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); String email = oAuth2User.getAttribute("email"); String fullName = oAuth2User.getAttribute("name"); if (!userService.existsMail(email) && !userService.checkUser(email)) { response.sendRedirect("http://localhost:3000/register?email=" + email + "&fullName=" + fullName); } else { response.sendRedirect("http://localhost:3000/home"); } } }
安全配置片段
http .oauth2Login(oauth2 -> oauth2 .successHandler(customOAuth2SuccessHandler) .failureUrl("http://localhost:3000/login") );
普通登录接口
@PostMapping("/login") public ResponseEntity<RestResponse<AuthResponse>> login(@RequestBody AuthRequest authRequest) { AuthResponse response = authService.login(authRequest); return new ResponseEntity<>(RestResponse.of(response), HttpStatus.OK); }
AuthResponse 记录类型
public record AuthResponse( Long userId, String username, String profileImageUrl, String accessToken, String refreshToken, String message ) {}
解决方案
方案一:无需重定向直接返回AuthResponse JSON
修改CustomOAuth2SuccessHandler,跳过重定向逻辑,直接构建AuthResponse并以JSON格式返回给客户端:
@Component public class CustomOAuth2SuccessHandler implements AuthenticationSuccessHandler { private final UserService userService; private final JwtService jwtService; private final RefreshTokenService refreshTokenService; private final ObjectMapper objectMapper; // 注入ObjectMapper用于JSON序列化 public CustomOAuth2SuccessHandler(UserService userService, JwtService jwtService, RefreshTokenService refreshTokenService, ObjectMapper objectMapper) { this.userService = userService; this.jwtService = jwtService; this.refreshTokenService = refreshTokenService; this.objectMapper = objectMapper; } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); String email = oAuth2User.getAttribute("email"); String fullName = oAuth2User.getAttribute("name"); String profileImage = oAuth2User.getAttribute("picture"); // Google OAuth返回的头像字段 // 检查用户是否存在 if (!userService.existsMail(email) && !userService.checkUser(email)) { // 若用户未注册,返回注册提示的AuthResponse AuthResponse authResponse = new AuthResponse(null, fullName, profileImage, null, null, "用户未注册,请完成注册"); writeJsonResponse(response, authResponse, HttpStatus.OK); return; } // 用户已存在,生成JWT和刷新令牌 User user = userService.getUserByEmail(email); String accessToken = jwtService.generateToken(user); RefreshToken refreshToken = refreshTokenService.createRefreshToken(user.getId()); // 构建AuthResponse AuthResponse authResponse = new AuthResponse( user.getId(), user.getUsername(), profileImage, accessToken, refreshToken.getToken(), "登录成功" ); // 返回JSON响应 writeJsonResponse(response, authResponse, HttpStatus.OK); } // 工具方法:将对象写入响应为JSON格式 private void writeJsonResponse(HttpServletResponse response, Object data, HttpStatus status) throws IOException { response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); response.setStatus(status.value()); objectMapper.writeValue(response.getWriter(), RestResponse.of(data)); } }
注意:前后端分离场景下,需确保Google OAuth2的回调地址配置为后端接口,同时处理跨域问题(如配置CORS或使用前端代理)。
方案二:重定向时传递AuthResponse信息
重定向是GET请求,无法直接传递JSON对象,可通过以下两种方式处理:
方式1:Base64编码JSON参数
将AuthResponse序列化为JSON后做Base64编码,作为重定向URL的查询参数传递,前端拿到后解码解析:
@Component public class CustomOAuth2SuccessHandler implements AuthenticationSuccessHandler { // 注入依赖... private final ObjectMapper objectMapper; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); String email = oAuth2User.getAttribute("email"); String fullName = oAuth2User.getAttribute("name"); String profileImage = oAuth2User.getAttribute("picture"); if (!userService.existsMail(email) && !userService.checkUser(email)) { response.sendRedirect("http://localhost:3000/register?email=" + email + "&fullName=" + fullName); return; } // 构建AuthResponse并转Base64 User user = userService.getUserByEmail(email); String accessToken = jwtService.generateToken(user); RefreshToken refreshToken = refreshTokenService.createRefreshToken(user.getId()); AuthResponse authResponse = new AuthResponse( user.getId(), user.getUsername(), profileImage, accessToken, refreshToken.getToken(), "登录成功" ); // 序列化为JSON并Base64编码 String jsonStr = objectMapper.writeValueAsString(RestResponse.of(authResponse)); String encodedResponse = Base64.getUrlEncoder().encodeToString(jsonStr.getBytes(StandardCharsets.UTF_8)); // 重定向到前端页面,携带编码后的参数 response.sendRedirect("http://localhost:3000/home?auth=" + encodedResponse); } }
前端解析示例:
const authParam = new URLSearchParams(window.location.search).get('auth'); if (authParam) { const decodedJson = atob(authParam); const authResponse = JSON.parse(decodedJson); // 处理用户信息和令牌 }
方式2:存入Session(适合同域场景)
如果前端和后端同域,可将AuthResponse存入HttpSession,前端重定向后通过接口获取Session中的信息:
@Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { // ... 构建AuthResponse逻辑 ... // 存入Session request.getSession().setAttribute("authResponse", authResponse); // 重定向到前端 response.sendRedirect("http://localhost:3000/home"); }
后端新增获取Session信息的接口:
@GetMapping("/auth/session") public ResponseEntity<RestResponse<AuthResponse>> getAuthFromSession(HttpServletRequest request) { AuthResponse authResponse = (AuthResponse) request.getSession().getAttribute("authResponse"); if (authResponse != null) { request.getSession().removeAttribute("authResponse"); // 用完清除 return ResponseEntity.ok(RestResponse.of(authResponse)); } return ResponseEntity.notFound().build(); }
内容的提问来源于stack exchange,提问作者demirs
相关产品推荐
相关产品推荐

