OAuth 使用双 Token 的原因¶
核心结论¶
- Access Token 会在网络请求中频繁传输(例如放在
Authorization头里),暴露面更大,泄露风险更高。 - Refresh Token 通常只保存在本地安全存储中(不随每次 API 请求发送),暴露面更小,不容易泄露。
设计意义¶
双 Token 机制的目标是: 1. 让高频使用的凭证(Access Token)短期有效,即使泄露影响窗口也有限。 2. 让低频使用的凭证(Refresh Token)低暴露、可续期,用于换新 Access Token。
一句话理解¶
把“经常在路上跑的钥匙”和“家里保险箱里的总钥匙”分开,安全性会更高。
流程图(登录 / 访问 / 续签)¶
flowchart TD
A[用户登录] --> B[认证服务器校验账号密码]
B --> C[签发 Access Token + Refresh Token]
C --> D[客户端安全存储 Token]
D --> E[调用业务 API\n携带 Access Token]
E --> F{Access Token 有效?}
F -- 是 --> G[资源服务器返回数据]
F -- 否/过期 --> H[客户端调用刷新接口\n携带 Refresh Token]
H --> I{Refresh Token 有效?}
I -- 是 --> J[认证服务器签发新的 Access Token\n(可选轮换 Refresh Token)]
J --> E
I -- 否/过期/撤销 --> K[要求用户重新登录]
时序图(更细)¶
sequenceDiagram
participant U as User
participant C as Client
participant AS as Auth Server
participant RS as Resource Server
U->>C: 登录
C->>AS: 用户凭证
AS-->>C: Access Token + Refresh Token
C->>RS: API 请求 + Access Token
RS-->>C: 200 数据(Token 有效)
C->>RS: API 请求 + 过期 Access Token
RS-->>C: 401 Unauthorized
C->>AS: Refresh Token 换新
AS-->>C: 新 Access Token(可选新 Refresh Token)
C->>RS: 重试 API + 新 Access Token
RS-->>C: 200 数据
Refresh Token 存储建议(主流)¶
- 一般将 Refresh Token 保存在 HttpOnly Cookie。
- 理由:
- JS 读不到(
HttpOnly),XSS 更难直接窃取 Refresh Token。 - 浏览器会在同站策略允许下自动携带 Cookie,刷新流程更顺滑。
- 可实现“无感刷新”,用户体验更好。
无感刷新流程¶
flowchart TD
A[请求 API] --> B{Access Token 有效?}
B -- 是 --> C[返回数据]
B -- 否 --> D[返回 401]
D --> E[调用 /refresh\n浏览器自动带 refresh cookie]
E --> F{Refresh Token 有效?}
F -- 是 --> G[返回新 Access Token]
G --> H[重试原请求]
H --> I[成功]
F -- 否 --> J[跳转登录页]
口诀(便于记忆)¶
- Access Token 是“干活权限”。
- Refresh Token 是“续命权限”。
- 关键风控点通常发生在 refresh 过程(例如设备校验、IP/地理异常、令牌轮换与重放检测)。
React Native 存储建议¶
✅ 最优实践:系统安全存储(Keychain / Keystore)
- iOS:使用 Keychain
- Android:使用 Keystore(通常由库封装为加密后的本地存储,如加密 SharedPreferences)
RN 侧推荐: - react-native-keychain(通用、成熟) - expo-secure-store(Expo 体系更顺)