前言
做海外项目时,Google 一键登录几乎是标配。相比传统的邮箱注册流程(填邮箱→收验证码→设密码),用户只需要点一下 Google 按钮就能完成注册/登录,转化率能高不少。
本文以我自己的一个 Go 后端项目为例,分享 Google 一键登录的后端实现,技术栈是 GoFrame + JWT + MySQL。前端用的是 Google 官方的 Sign In With Google 按钮,后端只需要验证前端传过来的 ID Token 就行。
整体流程
Google 一键登录的核心流程其实很简单:
sequenceDiagram
participant U as 用户(浏览器)
participant G as Google
participant B as 后端
U->>G: 1. 点击 Google 登录按钮,弹出授权窗口
G->>G: 2. 用户在 Google 页面完成身份验证
G->>U: 3. 返回 ID Token(JWT 格式)
U->>B: 4. POST /user/google-login {idToken}
B->>G: 5. 拉取 Google JWKS 公钥(有缓存)
G-->>B: 6. 返回公钥
B->>B: 7. 用公钥本地验证 ID Token 签名和有效期
B->>B: 8. 根据 googleID 查找/创建用户,签发 JWT
B->>U: 9. 返回 JWT + 用户信息
后端要做的事只有三件:
- 验证 ID Token——确认这个 token 确实是 Google 颁发的,且是给你的应用的
- 查找或创建用户——第一次登录自动注册,后续登录直接查
- 签发自己的 JWT——后续请求都走自己的鉴权体系
前置准备:Google Cloud Console 配置
环境依赖:
- Go 1.24
- GoFrame v2.10.0
- google.golang.org/api v0.259.0
- github.com/golang-jwt/jwt/v5 v5.3.0
在写代码之前,你需要去 Google Cloud Console 创建一个 OAuth 2.0 客户端:
- 进入 APIs & Services → Credentials
- 点 Create Credentials → OAuth client ID
- 应用类型选 Web application
- 配置 Authorized JavaScript origins(你的前端域名,比如
https://www.example.com) - 创建后拿到 Client ID 和 Client Secret
后端验证 ID Token 只需要 Client ID,不需要 Client Secret。
配置文件里加上:
1 | google: |
后端实现
1. 定义 API 接口
用 GoFrame 的结构化 API 定义:
1 | type GoogleLoginReq struct { |
前端只需要传一个 idToken 字段,就是 Google Sign In 回调里拿到的 credential。
2. 核心逻辑:验证 Token + 查建用户
这是整个 Google 登录的核心代码:
1 | package user |
逐步拆解:
验证 ID Token
1 | payload, err := idtoken.Validate(ctx, req.IDToken, clientID) |
google.golang.org/api/idtoken 包帮你做了所有脏活:
- 从 Google 的 JWKS 端点拉取公钥
- 验证 token 签名
- 检查
aud(audience)是否匹配你的 Client ID - 检查 token 是否过期
验证通过后,payload.Subject 就是用户的 Google ID(全局唯一且不变),payload.Claims 里有 email、name、picture 等信息。
查找或创建用户
用 google_id 作为关联字段查用户表。如果查不到说明是新用户,自动创建一条记录。这里用了 Snowflake 算法生成分布式唯一 ID 作为业务主键。
为什么用 google_id 而不是 email 来关联? 因为用户可能会改 Google 账号的邮箱,但 Subject(Google ID)是永远不变的。
签发 JWT
验证完 Google 身份后,后续的鉴权还是走自己的 JWT 体系。这样做的好处是:
- 不依赖 Google 的 token 有效期
- 可以统一处理邮箱注册和 Google 注册的用户
- 中间件只需要解析自己的 JWT,不用区分登录方式
3. JWT 签发与验证
1 | func GenerateToken(uid, email string) (string, error) { |
每个 JWT 都有一个 UUID 作为 jti(JWT ID),配合 Redis 黑名单可以实现主动吊销 token(比如用户退出登录时)。
4. 路由注册
Google 登录接口放在无需鉴权的公开路由组里:
1 | group.POST("/user/google-login", user.NewV1().GoogleLogin) |
跟普通的邮箱登录接口并列,登录成功后返回的数据格式完全一致——前端拿到 token 存起来,后续请求带上就行。
数据库设计要点
users 表需要加几个字段:
1 | ALTER TABLE users ADD COLUMN google_id VARCHAR(128) DEFAULT NULL COMMENT 'Google用户ID'; |
google_id加唯一索引,防止重复绑定register_type区分注册来源,后续做数据分析有用email字段对 Google 用户也保留,方便发通知邮件
安全注意事项
- 永远在后端验证 ID Token——前端传过来的 token 不可信,必须后端调 Google API 验证
- 校验 audience——
idtoken.Validate的第三个参数就是你的 Client ID,防止其他应用的 token 被拿来登录你的系统 - 不要把 Client Secret 暴露到前端——ID Token 验证方案不需要 Client Secret,但如果你用 Authorization Code 方案就需要,记得只放后端
- google_id 用唯一索引——防止并发场景下创建重复用户
前端怎么接?
前端用的是 Google Identity Services(GIS),这是 Google 2021 年推出的新版登录 SDK,取代了老版的 google-signin 库。整个接入只需三步。
第一步:引入 GIS 脚本
在 HTML 的 <head> 里加上:
1 | <script src="https://accounts.google.com/gsi/client" async defer></script> |
async defer 让脚本异步加载,不阻塞页面渲染。
第二步:放置登录按钮
有两种方式,按需选一种:
方式一:纯 HTML(最简单)
直接在页面放两个 div,GIS 脚本加载后会自动处理:
1 | <!-- 配置项:Client ID 和回调函数名 --> |
data-client_id:换成你在 Google Cloud Console 拿到的 Client IDdata-callback:登录成功后调用的 JavaScript 函数名g_id_signin的data-theme、data-size等属性控制按钮外观,Google 官方文档有完整参数列表
方式二:JavaScript API(更灵活)
需要在特定时机触发登录、或者想启用 One Tap 弹窗时,用 JS 控制更方便:
1 | window.onload = function () { |
1 | <div id="googleSignInBtn"></div> |
第三步:处理回调、调后端接口
登录成功后,Google 会调用你配置的回调函数,参数里的 credential 字段就是 ID Token——一段很长的 JWT 字符串,直接传给后端就行:
1 | function handleCredentialResponse(response) { |
完整示例
把上面三步拼在一起,一个能直接跑的最简登录页面:
1 |
|
几个容易踩的坑
本地开发必须用 http://localhost
在 Google Cloud Console 配置 Authorized JavaScript origins 时,本地开发要加 http://localhost(不是 127.0.0.1,也不是 http://localhost:3000 这样带端口的)。否则会报 idpiframe_initialization_failed 错误。
不要在 file:// 协议下测试
GIS 不支持 file://,必须通过 HTTP/HTTPS 服务器访问。本地可以用 python3 -m http.server 8000 起一个简单服务器。
跨域问题
前后端分域部署时(比如前端 app.example.com,后端 api.example.com),后端 /user/google-login 接口要配置 CORS,允许前端域名,否则 fetch 会被浏览器拦截。
response.credential 是一次性的,不要在前端 decode
ID Token 有效期很短(约 1 小时),而且里面的内容只有后端拿公钥验过签之后才可信。不要在前端用 atob 或 jwt-decode 库解析它来做业务判断,直接传给后端就行。
ID Token 方案 vs Authorization Code 方案
Google 一键登录的后端实现并不复杂,核心就是 idtoken.Validate 这一步。整个流程:
- 前端拿到 Google 的 ID Token
- 后端用
google.golang.org/api/idtoken包验证 - 用
payload.Subject关联/创建用户 - 签发自己的 JWT 返回
相比 OAuth2 Authorization Code 方案,ID Token 方案更简单——不需要后端跟 Google 换 access token,一个 HTTP 请求都不用发(idtoken 包会缓存 Google 的公钥)。适合”只需要登录,不需要调 Google API”的场景。