# 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
→ 进入主界面
```

---

## 五、接口请求拦截

所有需要登录的接口都必须携带：

```http
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  │  │ 清空数据  │
│ 进主页  │  │ 进登录页  │
└────────┘  └──────────┘
```
