8.9 KiB
8.9 KiB
Google Identity Services (GIS) 登录集成文档
概述
使用最新的 Google Identity Services (GIS) SDK 实现 Google 登录,无需页面跳转,通过弹窗方式完成授权,用户体验更好。
新旧方式对比
旧方式(OAuth 2.0 重定向流程)
用户点击按钮 → 跳转到 Google 授权页面 → 授权后重定向回应用
❌ 需要页面跳转
❌ 需要配置回调路由
❌ 用户体验不连贯
新方式(Google Identity Services)
用户点击按钮 → 弹出 Google 授权窗口 → 授权后直接回调
✅ 无需页面跳转
✅ 无需回调路由
✅ 用户体验流畅
✅ 更安全(弹窗隔离)
实现架构
工作流程
用户点击 "Continue with Google"
↓
加载 Google Identity Services SDK
↓
初始化 Code Client
↓
弹出 Google 授权窗口
↓
用户授权
↓
回调函数接收授权码
↓
调用后端登录接口
↓
登录成功,跳转到首页
核心文件
1. Google OAuth 配置 (src/lib/oauth/google.ts)
主要功能:
- 定义 Google Identity Services 的 TypeScript 类型
- 提供 SDK 加载方法
- 提供 Code Client 初始化方法
关键代码:
export const googleOAuth = {
// 加载 Google Identity Services SDK
loadScript: (): Promise<void> => {
return new Promise((resolve, reject) => {
if (window.google?.accounts) {
resolve()
return
}
const script = document.createElement('script')
script.src = 'https://accounts.google.com/gsi/client'
script.async = true
script.defer = true
script.onload = () => resolve()
script.onerror = () => reject(new Error('Failed to load SDK'))
document.head.appendChild(script)
})
},
// 初始化 Code Client(获取授权码)
initCodeClient: (callback, errorCallback) => {
return window.google.accounts.oauth2.initCodeClient({
client_id: GOOGLE_CLIENT_ID,
scope: GOOGLE_SCOPES,
ux_mode: 'popup',
callback,
error_callback: errorCallback
})
}
}
2. GoogleButton 组件 (src/app/(auth)/login/components/GoogleButton.tsx)
主要功能:
- 加载 Google Identity Services SDK
- 初始化 Code Client
- 处理授权码回调
- 调用后端登录接口
关键实现:
SDK 加载
useEffect(() => {
const loadGoogleSDK = async () => {
try {
await googleOAuth.loadScript()
console.log('Google Identity Services SDK loaded')
} catch (error) {
console.error('Failed to load Google SDK:', error)
toast.error("Failed to load Google login")
}
}
loadGoogleSDK()
}, [])
授权码处理
const handleGoogleResponse = async (response: GoogleCodeResponse) => {
const deviceId = tokenManager.getDeviceId()
const loginData = {
appClient: AppClient.Web,
deviceCode: deviceId,
thirdToken: response.code, // Google 授权码
thirdType: ThirdType.Google
}
login.mutate(loginData, {
onSuccess: () => {
toast.success("Login successful")
router.push('/')
},
onError: (error) => {
toast.error("Login failed")
}
})
}
登录按钮点击
const handleGoogleLogin = async () => {
// 确保 SDK 已加载
if (!window.google?.accounts?.oauth2) {
await googleOAuth.loadScript()
}
// 初始化 Code Client
if (!codeClientRef.current) {
codeClientRef.current = googleOAuth.initCodeClient(
handleGoogleResponse,
handleGoogleError
)
}
// 请求授权码(弹出授权窗口)
codeClientRef.current.requestCode()
}
环境变量配置
只需要配置客户端 ID,不需要配置回调 URL:
# .env.local
NEXT_PUBLIC_GOOGLE_CLIENT_ID=你的Google客户端ID
获取 Google OAuth 凭据
- 访问 Google Cloud Console
- 创建或选择项目
- 启用 Google+ API
- 创建 OAuth 2.0 客户端 ID
- 应用类型选择 "Web 应用"
- 授权的 JavaScript 来源添加:
http://localhost:3000 https://test.crushlevel.ai - 授权的重定向 URI 可以留空(GIS 不需要)
- 复制客户端 ID
后端接口要求
与之前相同,后端接收授权码并完成登录:
POST /api/auth/login
{
"appClient": "WEB",
"deviceCode": "设备ID",
"thirdToken": "Google授权码",
"thirdType": "GOOGLE"
}
后端需要:
- 使用授权码向 Google 交换 access_token
- 使用 access_token 获取用户信息
- 创建或更新用户
- 返回应用的登录 token
优势
1. 更好的用户体验
- ✅ 无需离开当前页面
- ✅ 弹窗授权,快速完成
- ✅ 不打断用户操作流程
2. 更简单的实现
- ✅ 不需要回调路由
- ✅ 不需要处理 URL 参数
- ✅ 不需要 state 验证
- ✅ 代码更简洁
3. 更安全
- ✅ 弹窗隔离,防止钓鱼
- ✅ SDK 自动处理安全验证
- ✅ 支持 CORS 和 CSP
4. 更现代
- ✅ Google 官方推荐方式
- ✅ 持续维护和更新
- ✅ 更好的浏览器兼容性
与旧实现的对比
| 特性 | 旧方式(重定向) | 新方式(GIS) |
|---|---|---|
| 页面跳转 | ✅ 需要 | ❌ 不需要 |
| 回调路由 | ✅ 需要 | ❌ 不需要 |
| State 验证 | ✅ 需要手动实现 | ❌ SDK 自动处理 |
| URL 参数处理 | ✅ 需要 | ❌ 不需要 |
| 用户体验 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 代码复杂度 | 高 | 低 |
| 维护成本 | 高 | 低 |
常见问题
Q: SDK 加载失败怎么办?
A:
- 检查网络连接
- 确认没有被广告拦截器阻止
- 检查浏览器控制台错误信息
Q: 弹窗被浏览器拦截?
A:
- 确保在用户点击事件中调用
requestCode() - 不要在异步操作后调用
- 检查浏览器弹窗设置
Q: 授权后没有回调?
A:
- 检查回调函数是否正确绑定
- 查看浏览器控制台是否有错误
- 确认 Client ID 配置正确
Q: 用户取消授权如何处理?
A:
const handleGoogleError = (error: any) => {
// 用户取消授权不显示错误提示
if (error.type === 'popup_closed') {
return
}
toast.error("Google login failed")
}
测试清单
本地测试
- SDK 正常加载
- 点击按钮弹出授权窗口
- 授权后正确回调
- 授权码正确传递给后端
- 登录成功后正确跳转
- 用户取消授权的处理
- 错误情况的处理
生产环境测试
- 配置正确的 JavaScript 来源
- HTTPS 证书有效
- 环境变量配置正确
- 后端接口正常工作
- 不同浏览器测试
浏览器兼容性
Google Identity Services 支持:
- ✅ Chrome 90+
- ✅ Firefox 88+
- ✅ Safari 14+
- ✅ Edge 90+
安全注意事项
1. 客户端 ID 保护
虽然客户端 ID 是公开的,但仍需注意:
- 限制授权的 JavaScript 来源
- 定期检查使用情况
- 发现异常及时更换
2. 授权码处理
- 授权码只能使用一次
- 及时传递给后端
- 不要在客户端存储
3. HTTPS 要求
- 生产环境必须使用 HTTPS
- 本地开发可以使用 HTTP
迁移指南
如果你之前使用的是旧的重定向方式,迁移步骤:
-
更新配置文件
- 使用新的
src/lib/oauth/google.ts
- 使用新的
-
更新组件
- 使用新的
GoogleButton.tsx
- 使用新的
-
删除回调路由
- 删除
src/app/api/auth/google/callback/route.ts
- 删除
-
更新 Google Cloud Console
- 添加授权的 JavaScript 来源
- 可以移除重定向 URI(可选)
-
测试
- 完整测试登录流程
- 确认所有功能正常
扩展功能
1. One Tap 登录
可以添加 Google One Tap 功能,自动显示登录提示:
window.google.accounts.id.initialize({
client_id: GOOGLE_CLIENT_ID,
callback: handleCredentialResponse
})
window.google.accounts.id.prompt()
2. 自动登录
可以实现自动登录功能:
window.google.accounts.id.initialize({
client_id: GOOGLE_CLIENT_ID,
callback: handleCredentialResponse,
auto_select: true
})
3. 自定义按钮样式
可以使用 Google 提供的标准按钮:
window.google.accounts.id.renderButton(
document.getElementById('buttonDiv'),
{ theme: 'outline', size: 'large' }
)
相关文档
总结
使用 Google Identity Services 是 Google 官方推荐的最新方式,相比传统的 OAuth 重定向流程:
✅ 用户体验更好 - 无需页面跳转
✅ 实现更简单 - 代码量更少
✅ 维护更容易 - 无需处理复杂的回调
✅ 更加安全 - SDK 自动处理安全验证
强烈建议新项目直接使用这种方式!