Java 实现Google 账号单点登录(OAuth 2.0)全流程解析

Luca Ju
2025-09-19 / 0 评论 / 231 阅读 / 正在检测是否收录...

在当今的互联网应用中,单点登录(SSO)已成为提升用户体验的重要功能 —— 用户无需重复注册账号,通过常用的第三方平台(如 Google、GitHub)即可快速登录。本文将详细介绍如何基于 OAuth 2.0 协议,使用 Java 结合 JustAuth 框架实现 Google 账号单点登录,涵盖从 Google 平台配置到前后端代码落地的完整流程。

一、前置知识:Google 账号登录核心原理

Google 账号登录基于 OAuth 2.0 授权协议,核心流程可概括为 3 步:

  1. 应用引导用户跳转至 Google 授权页面,用户完成身份验证并同意授权;
  2. Google 授权服务器向应用的回调地址返回授权码;
  3. 应用通过授权码向 Google 申请访问令牌,进而获取用户基本信息,最终完成登录逻辑。

本文将使用 JustAuth 框架(轻量级 OAuth 工具包)简化开发,避免重复编写 OAuth 协议相关的底层代码。

二、前期准备:Google Cloud 平台服务配置

要实现 Google 登录,首先需要在 Google Cloud 平台创建应用并配置授权信息,这是整个流程的基础。

1. 登录 Google Cloud 平台

访问 Google Cloud 控制台,使用 Google 账号登录。

2. 创建 / 选择项目

  • 若已有项目:直接在顶部项目列表中选择目标项目;
  • 若新建项目:点击顶部「项目」→「新建项目」,输入项目名称(如「Java-Google-SSO-Demo」),完成创建后等待项目初始化(约 1-2 分钟)。

create-project

3. 配置「OAuth 同意屏幕」

新创建的项目必须先配置「同意屏幕」,否则无法创建客户端凭证。

screen-config

  1. 在左侧菜单找到「API 和服务」→「OAuth 同意屏幕」;
  2. 选择「外部」(适用于面向公众的应用,内部应用仅适用于企业域内用户),点击「创建」;

    screen-2

  3. 填写「应用信息」:
  • 应用名称:将显示在 Google 登录授权页面(如「我的 Java 应用」);
  • 支持邮箱:用于接收 Google 相关通知;
  1. 后续步骤(范围、测试用户、摘要)保持默认配置,点击「提交」即可(测试阶段无需审核,直接可用)。

4. 创建 Client 客户端(关键!)

客户端凭证(Client ID、Client Secret)是应用与 Google 授权服务器通信的身份标识,需重点配置。

  1. 在左侧菜单找到「API 和服务」→「凭据」→「创建凭据」→「OAuth 客户端 ID」;

    create-oauth

  2. 应用类型选择「Web 应用」,填写名称(如「Java-Web-SSO」);

    screen-3

  3. 配置回调地址(Redirect URI)
  1. 点击「创建」,记录生成的 Client IDClient Secret(后续代码会用到,切勿泄露)。

三、前端实现:Google 登录按钮与跳转

前端只需提供一个登录按钮,点击后跳转至后端接口,由后端引导至 Google 授权页面(避免前端直接与 Google 交互,保证安全性)。

本文以 Vue 为例,其他框架(React、Angular)逻辑一致:

<template>
  <!-- 页面中的 Google 登录按钮 -->
  <button 
    class="google-login-btn" 
    @click="handleGoogleLogin"
  >
    <i class="icon-google"></i> 用 Google 账号登录
  </button>
</template>

<script>
export default {
  name: 'LoginPage',
  methods: {
    /**
     * 处理 Google 登录点击事件
     * 核心:跳转至后端接口,由后端重定向到 Google 授权页
     */
    handleGoogleLogin() {
      // 后端接口地址(需与后端服务端口一致,如本地 8080 端口)
      window.location.href = "http://localhost:8080/google-login";
    }
  }
};
</script>

<style scoped>
.google-login-btn {
  padding: 8px 16px;
  background: #4285F4;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>

注意:此处需使用 window.location.href 进行页面级跳转,而非异步请求(axios/fetch)—— 因为 Google 授权页面需要用户手动交互(输入账号密码、同意授权)。

四、后端实现:基于 JustAuth 框架的核心逻辑

后端使用 Spring Boot + JustAuth 框架,主要实现两个接口:

  1. google-login:引导用户跳转至 Google 授权页;
  2. google-login-callback:接收 Google 回调,处理授权逻辑并完成登录。

1. 第一步:添加 JustAuth 依赖

在 pom.xml 中引入 JustAuth 最新版本(版本可从 JustAuth GitHub 发布页 获取):

<!-- JustAuth:轻量级 OAuth 工具包,简化第三方登录开发 -->
<dependency>
  <groupId>me.zhyd.oauth</groupId>
  <artifactId>JustAuth</artifactId>
  <version>1.16.5</version> <!-- 替换为最新版本 -->
</dependency>

<!-- 若使用 Spring Boot,需确保已引入 web 依赖 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2. 第二步:实现「引导授权」接口(/google-login)

该接口的作用是:接收前端登录请求,生成 Google 授权地址,并将页面重定向至该地址。

import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.request.AuthGoogleRequest;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;

@RestController
  @AllArgsConstructor
public class AuthController {

    /**
     * Google 登录引导:重定向至 Google 授权页
     * @param response 用于重定向的响应对象
     * @throws IOException 重定向异常
     */
    @GetMapping("/google-login")
    public void googleLogin(HttpServletResponse response) throws IOException {
        // 1. 创建 Google 授权请求对象
        AuthRequest authRequest = getAuthRequest();
        // 2. 生成授权地址(包含随机 State,防止 CSRF 攻击)
        String googleAuthUrl = authRequest.authorize(AuthStateUtils.createState());
        // 3. 重定向至 Google 授权页
        response.sendRedirect(googleAuthUrl);
    }

    /**
     * 封装 Google 授权配置(抽离为工具方法,便于复用)
     * @return AuthRequest 授权请求对象
     */
    private AuthRequest getAuthRequest() {
        return new AuthGoogleRequest(AuthConfig.builder()
                // 1. 填入 Google Cloud 配置的 Client ID
                .clientId("你的 Client ID")
                // 2. 填入 Google Cloud 配置的 Client Secret
                .clientSecret("你的 Client Secret")
                // 3. 填入 Google Cloud 配置的回调地址(必须一致!)
                .redirectUri("http://localhost:8080/google-login-callback")
                // 4. (可选)国内环境配置代理(Google 服务需科学上网)
                .httpConfig(HttpConfig.builder()
                        .timeout(15000) // 超时时间:15秒
                        .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7897))) // 本地代理地址
                        .build())
                .build());
    }
}

关键说明

  • AuthStateUtils.createState():生成随机 State 参数,用于防止跨站请求伪造(CSRF)攻击,Google 会在回调时带回该参数,JustAuth 会自动校验;
  • 代理配置:国内环境访问 Google 服务需配置代理(如 Clash、V2Ray),需将 host 和 port 替换为本地代理的实际参数;
  • Client ID/Secret/Redirect URI:必须与 Google Cloud 配置完全一致,否则会报「无效客户端」或「回调地址不匹配」错误。

3. 第三步:实现「回调处理」接口(/google-login-callback)

用户在 Google 授权页完成操作后,Google 会将页面重定向至我们配置的回调地址,并携带 授权码(code)和 State 参数。该接口需完成以下逻辑:

  1. 接收 Google 回调参数;
  2. 通过 JustAuth 解析授权码,获取用户信息;
  3. 结合业务逻辑完成登录(如创建用户、生成自定义 Token);
  4. 重定向至前端首页(携带自定义 Token)。
import me.zhyd.oauth.model.AuthResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController
public class AuthController {

    // 从配置文件读取前端首页地址(如:http://localhost:8081/home?token=)
    @Value("${login.redirect.page}")
    private String loginRedirectPage;

    // 注入业务层服务(处理用户登录逻辑)
    private final AuthService authService;
    public AuthController(AuthService authService) {
        this.authService = authService;
    }

    /**
     * Google 登录回调:处理授权逻辑,完成登录
     * @param callback Google 回调参数(包含 code、state 等)
     * @param response 用于重定向至前端首页
     * @throws IOException 重定向异常
     */
    @GetMapping("/google-login-callback")
    public void googleLoginCallback(AuthCallback callback, HttpServletResponse response) throws IOException {
        // 1. 创建授权请求对象(复用之前的配置)
        AuthRequest authRequest = getAuthRequest();
        // 2. 解析回调参数,获取 Google 用户信息(JustAuth 自动处理 code 换 token、拉取用户信息)
        AuthResponse authResponse = authRequest.login(callback);
        
        // 3. 业务层处理:结合 Google 用户信息完成登录(核心逻辑)
        // 自定义 VO:包含系统生成的 Token、用户信息等
        AuthLoginRespVO loginResult = authService.doGoogleLogin(authResponse);
        
        // 4. 重定向至前端首页,并携带系统 Token(前端通过 Token 验证登录状态)
        response.sendRedirect(loginRedirectPage + loginResult.getAccessToken());
    }

    // (省略 getAuthRequest() 方法,与上文一致)
}

4. 补充:业务层登录逻辑(AuthService)

doGoogleLogin 方法是业务核心,需根据项目需求实现(如「用户首次登录自动创建账号」「已存在用户直接关联登录」)。以下是一个简化示例:

import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import org.springframework.stereotype.Service;

@Service
public class AuthService {

    /**
     * Google 登录业务逻辑
     * @param authResponse JustAuth 返回的 Google 用户信息
     * @return AuthLoginRespVO 系统登录结果(含 Token)
     */
    public AuthLoginRespVO doGoogleLogin(AuthResponse authResponse) {
        // 1. 解析 Google 用户信息(AuthUser 包含用户名、邮箱、头像等)
        AuthUser googleUser = (AuthUser) authResponse.getData();
        String googleOpenId = googleUser.getUuid(); // Google 用户唯一标识(OpenID)
        String googleEmail = googleUser.getEmail(); // Google 邮箱(需用户授权才能获取)

        // 2. 数据库查询:判断用户是否已关联 Google 账号
        User existingUser = userMapper.selectByGoogleOpenId(googleOpenId);

        User loginUser;
        if (existingUser != null) {
            // 3. 已关联用户:直接更新登录时间等信息
            existingUser.setLastLoginTime(new Date());
            userMapper.updateById(existingUser);
            loginUser = existingUser;
        } else {
            // 4. 未关联用户:创建新用户(或引导绑定已有账号)
            loginUser = new User();
            loginUser.setGoogleOpenId(googleOpenId);
            loginUser.setUsername(googleUser.getNickname());
            loginUser.setEmail(googleEmail);
            loginUser.setAvatar(googleUser.getAvatar());
            loginUser.setCreateTime(new Date());
            userMapper.insert(loginUser);
        }

        // 5. 生成系统自定义 Token(如 JWT Token)
        String systemToken = jwtUtils.generateToken(loginUser.getId());

        // 6. 返回登录结果(Token + 用户基本信息)
        return new AuthLoginRespVO()
                .setAccessToken(systemToken)
                .setUsername(loginUser.getUsername())
                .setAvatar(loginUser.getAvatar());
    }
}

五、整体流程回顾

  1. 用户点击前端「Google 登录」按钮,跳转至后端 /google-login 接口;
  2. 后端生成 Google 授权地址,重定向至 Google 授权页;
  3. 用户在 Google 页输入账号密码,同意授权后,Google 重定向至后端 /google-login-callback 接口,并携带 code;
  4. 后端通过 code 向 Google 申请 Token,拉取用户信息;
  5. 后端结合用户信息完成登录逻辑,生成系统 Token;
  6. 后端重定向至前端首页,携带 Token;
  7. 前端解析 Token,验证登录状态,跳转至首页。

六、常见问题与注意事项

  1. 回调地址不匹配:检查 Google Cloud 配置的 Redirect URI 与后端 redirectUri 是否完全一致(包括协议、域名、端口);
  2. 国内环境无法访问 Google:需配置代理(如上文的 httpConfig),确保后端服务能正常调用 Google 授权接口;
  3. 用户信息无法获取:需在 Google Cloud「OAuth 同意屏幕」中添加「邮箱」「个人资料」等权限范围;
  4. State 验证失败:JustAuth 会自动校验 State,若报错需检查是否在授权过程中修改了 State 参数;
  5. Client Secret 泄露风险:Client Secret 需存储在后端配置文件(如 application.yml),切勿暴露在前端代码中。

七、总结

本文基于 OAuth 2.0 协议和 JustAuth 框架,实现了 Google 账号单点登录的完整流程,核心在于「Google 平台配置」「授权引导」「回调处理」三个环节。JustAuth 框架极大简化了 OAuth 协议的底层实现,让开发者可以专注于业务逻辑(如用户关联、Token 生成)。

实际项目中,还可根据需求扩展功能,如「账号绑定」「登录日志记录」「Token 过期刷新」等。希望本文能为你的第三方登录开发提供参考!

2

评论 (0)

取消