4.3 KiB
4.3 KiB
iOS 登录流程 —— iOS 端集成
一、iOS 需要的核心组件
AuthService ← 调后端登录接口
UserService ← 用户信息管理
TokenStore ← token 存储协议
KeychainTokenStore ← 基于 Keychain 的安全存储实现
AppSession ← 管理当前登录态
二、数据存储策略
| 数据 | 存储位置 | 生命周期 | 原因 |
|---|---|---|---|
accessToken |
内存 | App 运行期间 | 短期使用,不需要持久化 |
refreshToken |
Keychain | 长期持久化 | 敏感凭证,需安全存储,卸载后也保留 |
user |
AppSession / UserStore | App 运行期间 | 用户展示信息 |
三、App 启动流程
App 启动
→ AppSession.checkSession()
→ 从 Keychain 读取 refreshToken
→ 如果没有 refreshToken
→ 进入登录页
→ 如果有 refreshToken
→ 调用 POST /api/auth/refresh
→ 成功
→ 存储新的 accessToken + refreshToken
→ 调用 GET /api/users/me
→ 存储 user 信息
→ 进入主界面
→ 失败
→ 清空 Keychain + 内存 token
→ 进入登录页
四、登录流程
开发登录(dev-login)
用户在登录页输入邮箱/昵称
→ AuthService.devLogin(email, nickname)
→ POST /api/auth/dev-login
→ 后端返回 { accessToken, refreshToken, user }
→ refreshToken 存 Keychain
→ accessToken 放内存
→ user 放 AppSession
→ 进入主界面
Apple 登录
用户点击 Sign in with Apple
→ iOS 系统弹出 Apple 授权界面
→ 用户授权成功
→ 拿到 identityToken + authorizationCode 等
→ AuthService.appleLogin(identityToken, ...)
→ POST /api/auth/apple
→ 后端验证 Apple token,返回 { accessToken, refreshToken, user }
→ refreshToken 存 Keychain
→ accessToken 放内存
→ user 放 AppSession
→ 进入主界面
五、接口请求拦截
所有需要登录的接口都必须携带:
Authorization: Bearer {accessToken}
HTTP Client 封装建议
所有请求自动注入 Authorization Header
→ 从 AuthService 获取当前 accessToken
→ 自动添加到请求头
401 自动处理
接口返回 401
→ 调用 POST /api/auth/refresh
→ 成功
→ 更新 accessToken
→ 自动重试原请求
→ 失败
→ 清空 Keychain + 内存数据
→ 跳转登录页
重要:重试原请求时注意避免无限循环,设置最多重试 1 次。
六、退出登录
用户点击退出登录
→ AuthService.logout()
→ POST /api/auth/logout
Body: { refreshToken: 从 Keychain 取的 refreshToken }
Header: Authorization: Bearer accessToken
→ 后端标记 refreshToken revoked
→ iOS 端:
→ 清除 Keychain 中的 refreshToken
→ 清除内存中的 accessToken
→ 清除 AppSession 中的 user
→ 跳转登录页
七、Token 存储对比:UserDefaults vs Keychain
| UserDefaults | Keychain | |
|---|---|---|
| 安全性 | 低(明文存储) | 高(系统级加密) |
| 应用卸载后 | 数据被清除 | 可选保留(推荐保留) |
| 备份 | 包含在 iTunes/iCloud 备份中 | 仅加密备份 |
| 适用数据 | 非敏感偏好设置 | 密码、Token 等敏感凭据 |
结论:refreshToken 一定要用 Keychain 存储。
八、Session 状态机
App 启动
│
▼
┌─────────────────┐
│ 检查 Keychain │
│ 有 refreshToken? │
└───────┬─────────┘
│
┌───────┴───────┐
│ 有 │ 无
▼ ▼
┌─────────┐ ┌──────────┐
│ 调 refresh │ │ 进入登录页 │
│ 接口 │ └──────────┘
└─────┬─────┘
│
┌────┴────┐
│ 成功 │ 失败
▼ ▼
┌────────┐ ┌──────────┐
│ 调 /me │ │ 清空数据 │
│ 进主页 │ │ 进登录页 │
└────────┘ └──────────┘