4.0 KiB
4.0 KiB
iOS 登录流程 —— 数据库设计
一、users 表
用户主表,存储用户基础信息。
model User {
id String @id @default(cuid())
email String?
nickname String?
avatarUrl String?
role UserRole @default(USER)
status UserStatus @default(ACTIVE)
onboardingCompleted Boolean @default(false)
authAccounts AuthAccount[]
refreshTokens RefreshToken[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum UserRole {
USER
ADMIN
SUPER_ADMIN
}
enum UserStatus {
ACTIVE
DISABLED
DELETED
}
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String (cuid) | 主键 |
email |
String? | 邮箱,可选(Apple 登录首次可能提供) |
nickname |
String? | 昵称 |
avatarUrl |
String? | 头像 URL |
role |
UserRole | 角色,默认 USER |
status |
UserStatus | 状态,默认 ACTIVE |
onboardingCompleted |
Boolean | 是否完成引导,默认 false |
authAccounts |
关联 | 一对多关联 auth_accounts |
refreshTokens |
关联 | 一对多关联 refresh_tokens |
二、auth_accounts 表
记录用户通过什么方式(provider)登录,支持一个用户绑定多个登录方式。
model AuthAccount {
id String @id @default(cuid())
userId String
provider AuthProvider
providerUserId String
email String?
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([provider, providerUserId])
@@index([userId])
}
enum AuthProvider {
DEV
APPLE
}
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String (cuid) | 主键 |
userId |
String | 关联 users 表 |
provider |
AuthProvider | 登录提供商(DEV / APPLE) |
providerUserId |
String | 提供商侧的用户唯一 ID |
email |
String? | 提供商侧邮箱 |
@@unique([provider, providerUserId]) |
约束 | 同一个提供商的用户唯一 |
查找逻辑:
Apple 登录时:
provider = APPLE
providerUserId = identityToken 里校验出来的 sub
→ 如果不存在,创建 User + AuthAccount
→ 如果存在,直接找到对应 User
三、refresh_tokens 表
重要:refreshToken 不要明文存数据库,只存 hash。
model RefreshToken {
id String @id @default(cuid())
userId String
tokenHash String
expiresAt DateTime
revokedAt DateTime?
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
}
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String (cuid) | 主键,同时写入 JWT payload 作为 tokenId |
userId |
String | 关联 users 表 |
tokenHash |
String | refreshToken 的 hash 值(SHA-256) |
expiresAt |
DateTime | 过期时间 |
revokedAt |
DateTime? | 撤销时间(登出时设置) |
刷新时的校验链:
1. 解析 refreshToken JWT,拿到 userId + tokenId
2. 查 refresh_tokens 表,找到对应记录
3. 对比 tokenHash
4. 确认 revokedAt 为 null(未撤销)
5. 确认 expiresAt 未过期
6. 签发新的 accessToken(可选轮换新的 refreshToken)
登出时:将对应记录的 revokedAt 设为当前时间。
四、ER 关系总结
User (1) ──── (N) AuthAccount 一个用户可有多种登录方式
User (1) ──── (N) RefreshToken 一个用户可有多个活跃 refreshToken(多设备)
- 用户与登录方式是解耦的:用户是一个独立实体,通过
auth_accounts关联到具体的第三方身份。 - 这种设计天然支持未来扩展更多登录方式(如 Google、微信等),只需在
AuthProvider枚举中添加即可。