You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中Google OAuth2登录后返回/携带AuthResponse的实现

问题描述

我在Spring Boot应用中使用Google OAuth2认证,希望登录成功后返回AuthResponse对象。当前登录成功后会将用户重定向到指定URL,但有两个需求:

  1. 无需重定向,直接返回包含用户ID、用户名、头像URL及JWT的用户信息;
  2. 若仍使用重定向,如何在重定向过程中传递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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 23:30:59