173 lines
4.2 KiB
Markdown
173 lines
4.2 KiB
Markdown
# OAuth JWT 集成实现文档
|
||
|
||
## 🎯 实现目标
|
||
|
||
将飞书和 GitHub 的第三方 OAuth access_token 转换为项目自己的 JWT Token,实现统一的认证体系。
|
||
|
||
## 🏗️ 架构设计
|
||
|
||
### 用户数据模型扩展
|
||
|
||
扩展了 `User` 实体以支持多种登录方式:
|
||
|
||
```typescript
|
||
@Entity()
|
||
export class User {
|
||
@PrimaryGeneratedColumn()
|
||
id: number;
|
||
|
||
@Column({ unique: true })
|
||
username: string;
|
||
|
||
@Column({ nullable: true }) // OAuth 用户不需要密码
|
||
password: string;
|
||
|
||
@Column()
|
||
email: string;
|
||
|
||
@Column({ default: true })
|
||
isActive: boolean;
|
||
|
||
// OAuth 相关字段
|
||
@Column({ nullable: true })
|
||
provider: string; // 'local', 'github', 'lark'
|
||
|
||
@Column({ nullable: true })
|
||
providerId: string; // 第三方平台的用户ID
|
||
|
||
@Column({ nullable: true })
|
||
providerUsername: string; // 第三方平台的用户名
|
||
|
||
// JWT 相关字段
|
||
@Column({ nullable: true })
|
||
refreshToken: string;
|
||
|
||
@Column({ nullable: true })
|
||
refreshTokenExpires: Date;
|
||
}
|
||
```
|
||
|
||
### UserService 扩展
|
||
|
||
添加了 OAuth 用户专用的方法:
|
||
|
||
1. **`findUserByProvider(provider, providerId)`** - 根据第三方平台信息查找用户
|
||
2. **`createOAuthUser(provider, providerId, providerUsername, email)`** - 创建 OAuth 用户
|
||
|
||
### 用户名生成策略
|
||
|
||
为避免用户名冲突,OAuth 用户的用户名格式为:
|
||
- 格式:`{provider}_{providerUsername}`
|
||
- 示例:`github_octocat`, `lark_zhangsan`
|
||
- 如果冲突,自动添加数字后缀:`github_octocat_1`
|
||
|
||
## 🔄 OAuth 流程
|
||
|
||
### GitHub OAuth 流程
|
||
|
||
1. **登录入口**:`GET /github/login?redirectUri=<回调地址>`
|
||
2. **OAuth 回调**:`GET /github/callback?code=<授权码>`
|
||
3. **处理流程**:
|
||
```
|
||
授权码 → GitHub Access Token → 用户信息 → 查找/创建本地用户 → 生成 JWT Token
|
||
```
|
||
|
||
### 飞书 OAuth 流程
|
||
|
||
1. **登录入口**:`GET /lark/login?redirectUri=<回调地址>`
|
||
2. **OAuth 回调**:`GET /lark/callback?code=<授权码>`
|
||
3. **处理流程**:
|
||
```
|
||
授权码 → 飞书 Access Token → 用户信息 → 查找/创建本地用户 → 生成 JWT Token
|
||
```
|
||
|
||
## 📝 API 响应格式
|
||
|
||
### 成功响应
|
||
|
||
```json
|
||
{
|
||
"message": "GitHub login successful",
|
||
"user": {
|
||
"id": 1,
|
||
"username": "github_octocat",
|
||
"email": "octocat@github.com",
|
||
"provider": "github",
|
||
"providerUsername": "octocat"
|
||
},
|
||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"expires_in": 3600,
|
||
"expires_at": 1703123456789,
|
||
"refresh_token_expires_in": 604800,
|
||
"refresh_token_expires_at": 1703728256789
|
||
}
|
||
```
|
||
|
||
## 🛡️ 安全特性
|
||
|
||
1. **用户隔离** - OAuth 用户和本地用户完全分离
|
||
2. **唯一标识** - 使用 `provider + providerId` 作为唯一标识
|
||
3. **JWT 安全** - 使用项目统一的 JWT 密钥和过期策略
|
||
4. **缓存机制** - 第三方 token 和用户信息缓存,减少 API 调用
|
||
|
||
## 🔧 配置要求
|
||
|
||
### 环境变量
|
||
|
||
```env
|
||
# GitHub OAuth
|
||
GITHUB_CLIENT_ID=your_github_client_id
|
||
GITHUB_CLIENT_SECRET=your_github_client_secret
|
||
GITHUB_CALLBACK_URL=http://localhost:3030/auth/github/callback
|
||
|
||
# 飞书 OAuth
|
||
FEISHU_APP_ID=your_feishu_app_id
|
||
FEISHU_APP_SECRET=your_feishu_app_secret
|
||
|
||
# JWT
|
||
JWT_SECRET=your_jwt_secret
|
||
```
|
||
|
||
## 🧪 测试
|
||
|
||
访问测试页面:`http://localhost:8000/test-github-sso.html`
|
||
|
||
支持的登录方式:
|
||
- GitHub OAuth
|
||
- 飞书 OAuth
|
||
- Passport GitHub 策略(用于重定向场景)
|
||
|
||
## 🚀 使用示例
|
||
|
||
### 前端调用
|
||
|
||
```javascript
|
||
// GitHub 登录
|
||
window.location.href = 'http://localhost:3030/github/login?redirectUri=' +
|
||
encodeURIComponent(window.location.origin + '/callback');
|
||
|
||
// 飞书登录
|
||
window.location.href = 'http://localhost:3030/lark/login?redirectUri=' +
|
||
encodeURIComponent(window.location.origin + '/callback');
|
||
```
|
||
|
||
### 使用 JWT Token
|
||
|
||
```javascript
|
||
// 在后续请求中使用 JWT Token
|
||
fetch('/api/protected', {
|
||
headers: {
|
||
'Authorization': `Bearer ${access_token}`
|
||
}
|
||
});
|
||
```
|
||
|
||
## 📋 优势
|
||
|
||
1. **统一认证** - 所有用户最终都使用项目的 JWT Token
|
||
2. **类型安全** - 完整的 TypeScript 类型定义
|
||
3. **扩展性强** - 易于添加新的 OAuth 提供商
|
||
4. **数据一致性** - 统一的用户数据模型
|
||
5. **安全可靠** - 遵循 OAuth 2.0 和 JWT 最佳实践
|