crush-level-web/docs/GoogleOAuth-GIS.md

421 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 初始化方法
**关键代码**:
```typescript
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 加载
```typescript
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()
}, [])
```
#### 授权码处理
```typescript
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')
},
})
}
```
#### 登录按钮点击
```typescript
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
```bash
# .env.local
NEXT_PUBLIC_GOOGLE_CLIENT_ID=你的Google客户端ID
```
### 获取 Google OAuth 凭据
1. 访问 [Google Cloud Console](https://console.cloud.google.com/)
2. 创建或选择项目
3. 启用 Google+ API
4. 创建 OAuth 2.0 客户端 ID
5. 应用类型选择 "Web 应用"
6. **授权的 JavaScript 来源**添加:
```
http://localhost:3000
https://test.crushlevel.ai
```
7. **授权的重定向 URI** 可以留空GIS 不需要)
8. 复制客户端 ID
## 后端接口要求
与之前相同,后端接收授权码并完成登录:
```typescript
POST /api/auth/login
{
"appClient": "WEB",
"deviceCode": "设备ID",
"thirdToken": "Google授权码",
"thirdType": "GOOGLE"
}
```
后端需要:
1. 使用授权码向 Google 交换 access_token
2. 使用 access_token 获取用户信息
3. 创建或更新用户
4. 返回应用的登录 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:
```typescript
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
## 迁移指南
如果你之前使用的是旧的重定向方式,迁移步骤:
1. **更新配置文件**
- 使用新的 `src/lib/oauth/google.ts`
2. **更新组件**
- 使用新的 `GoogleButton.tsx`
3. **删除回调路由**
- 删除 `src/app/api/auth/google/callback/route.ts`
4. **更新 Google Cloud Console**
- 添加授权的 JavaScript 来源
- 可以移除重定向 URI可选
5. **测试**
- 完整测试登录流程
- 确认所有功能正常
## 扩展功能
### 1. One Tap 登录
可以添加 Google One Tap 功能,自动显示登录提示:
```typescript
window.google.accounts.id.initialize({
client_id: GOOGLE_CLIENT_ID,
callback: handleCredentialResponse,
})
window.google.accounts.id.prompt()
```
### 2. 自动登录
可以实现自动登录功能:
```typescript
window.google.accounts.id.initialize({
client_id: GOOGLE_CLIENT_ID,
callback: handleCredentialResponse,
auto_select: true,
})
```
### 3. 自定义按钮样式
可以使用 Google 提供的标准按钮:
```typescript
window.google.accounts.id.renderButton(document.getElementById('buttonDiv'), {
theme: 'outline',
size: 'large',
})
```
## 相关文档
- [Google Identity Services 官方文档](https://developers.google.com/identity/gsi/web)
- [Code Model 文档](https://developers.google.com/identity/oauth2/web/guides/use-code-model)
- [迁移指南](https://developers.google.com/identity/gsi/web/guides/migration)
## 总结
使用 Google Identity Services 是 Google 官方推荐的最新方式,相比传统的 OAuth 重定向流程:
**用户体验更好** - 无需页面跳转
**实现更简单** - 代码量更少
**维护更容易** - 无需处理复杂的回调
**更加安全** - SDK 自动处理安全验证
强烈建议新项目直接使用这种方式!