Skip to content

Latest commit

 

History

History
320 lines (261 loc) · 7.67 KB

File metadata and controls

320 lines (261 loc) · 7.67 KB

Access Token + Refresh Token 双令牌机制设计

1. 核心概念

Access Token(访问令牌)

  • 用途:用于访问受保护的 API 资源
  • 特点
    • 短期有效(建议:15分钟 - 2小时)
    • 包含用户基本信息(user_id, username)
    • 每次 API 请求都需要携带
    • 过期后需要刷新

Refresh Token(刷新令牌)

  • 用途:用于刷新过期的 Access Token
  • 特点
    • 长期有效(建议:7天 - 30天)
    • 不包含用户信息,只包含 token_id
    • 存储在服务端(Redis/数据库),可撤销
    • 仅用于刷新接口,不能直接访问 API

2. 工作流程

2.1 用户登录流程

1. 用户提交用户名和密码
   POST /api/v1/users/login
   {
     "username": "user123",
     "password": "password123"
   }

2. 服务端验证用户信息
   - 验证用户名和密码
   - 检查用户状态(是否激活)

3. 生成双令牌
   - 生成 Access Token(短期,如 2 小时)
   - 生成 Refresh Token(长期,如 7 天)
   - 将 Refresh Token 存储到 Redis/数据库

4. 返回令牌给客户端
   {
     "code": 0,
     "message": "success",
     "data": {
       "access_token": "eyJhbGciOiJIUzI1NiIs...",
       "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
       "expires_in": 7200,  // Access Token 过期时间(秒)
       "token_type": "Bearer",
       "user": {
         "id": 1,
         "username": "user123",
         "email": "user@example.com"
       }
     }
   }

2.2 访问 API 流程

1. 客户端携带 Access Token 访问 API
   GET /api/v1/users/profile
   Authorization: Bearer {access_token}

2. 服务端验证 Access Token
   - 解析 JWT token
   - 验证签名和过期时间
   - 提取用户信息

3. 返回 API 响应
   - Token 有效:返回请求的数据
   - Token 过期:返回 401,提示使用 Refresh Token 刷新

2.3 刷新 Token 流程

1. Access Token 过期,客户端使用 Refresh Token 刷新
   POST /api/v1/auth/refresh
   {
     "refresh_token": "eyJhbGciOiJIUzI1NiIs..."
   }

2. 服务端验证 Refresh Token
   - 验证 Refresh Token 签名和过期时间
   - 检查 Refresh Token 是否在 Redis/数据库中(是否被撤销)
   - 验证用户状态(是否仍然有效)

3. 生成新的 Access Token
   - 生成新的 Access Token
   - 可选择是否刷新 Refresh Token(旋转策略)

4. 返回新的 Access Token
   {
     "code": 0,
     "message": "success",
     "data": {
       "access_token": "eyJhbGciOiJIUzI1NiIs...",
       "refresh_token": "eyJhbGciOiJIUzI1NiIs...",  // 可选,如果使用旋转策略
       "expires_in": 7200
     }
   }

2.4 用户登出流程

1. 客户端请求登出
   POST /api/v1/auth/logout
   Authorization: Bearer {access_token}
   {
     "refresh_token": "eyJhbGciOiJIUzI1NiIs..."
   }

2. 服务端撤销 Refresh Token
   - 从 Redis/数据库中删除 Refresh Token
   - Access Token 无需处理(短期,自然过期)

3. 返回成功响应
   {
     "code": 0,
     "message": "success"
   }

3. 技术实现细节

3.1 Token 存储策略

方案 A:Redis 存储(推荐)

  • Access Token:不存储,使用 JWT 自包含特性
  • Refresh Token:存储在 Redis,key 格式:refresh_token:{token_id}
    • 存储内容:{"user_id": 1, "username": "user123", "expires_at": 1234567890}
    • TTL:与 Refresh Token 过期时间一致

方案 B:数据库存储

  • 创建 refresh_tokens
  • 存储 Refresh Token 信息,支持查询和撤销

3.2 Token 旋转策略

策略 1:不旋转(简单)

  • Refresh Token 刷新时,返回新的 Access Token
  • Refresh Token 保持不变,直到过期

策略 2:旋转(安全,推荐)

  • Refresh Token 刷新时,同时生成新的 Access Token 和 Refresh Token
  • 旧的 Refresh Token 立即失效
  • 提高安全性,防止 Refresh Token 被盗用

3.3 Token 撤销机制

  • 主动撤销:用户登出时删除 Refresh Token
  • 被动撤销:用户修改密码、账户被禁用时,删除所有 Refresh Token
  • 批量撤销:支持撤销用户的所有 Refresh Token

4. 安全考虑

4.1 Access Token 安全

  • 使用 HTTPS 传输
  • 短期有效,减少泄露风险
  • 不存储敏感信息
  • 支持黑名单机制(可选,需要存储)

4.2 Refresh Token 安全

  • 存储在服务端,可撤销
  • 长期有效,但可以主动撤销
  • 使用不同的密钥签名
  • 限制使用频率(防止暴力破解)

4.3 防止 Token 泄露

  • 使用 HTTPS
  • 设置合理的过期时间
  • 支持 Token 撤销
  • 记录 Token 使用日志(可选)

5. API 接口设计

5.1 登录接口

POST /api/v1/users/login
Request:
{
  "username": "string",
  "password": "string"
}

Response:
{
  "code": 0,
  "message": "success",
  "data": {
    "access_token": "string",
    "refresh_token": "string",
    "expires_in": 7200,
    "token_type": "Bearer",
    "user": {...}
  }
}

5.2 刷新 Token 接口

POST /api/v1/auth/refresh
Request:
{
  "refresh_token": "string"
}

Response:
{
  "code": 0,
  "message": "success",
  "data": {
    "access_token": "string",
    "refresh_token": "string",  // 如果使用旋转策略
    "expires_in": 7200
  }
}

5.3 登出接口

POST /api/v1/auth/logout
Headers:
  Authorization: Bearer {access_token}

Request:
{
  "refresh_token": "string"
}

Response:
{
  "code": 0,
  "message": "success"
}

6. 配置参数

6.1 Access Token 配置

  • JWT_ACCESS_SECRET: Access Token 签名密钥
  • JWT_ACCESS_EXPIRE: Access Token 过期时间(秒),建议 7200(2小时)

6.2 Refresh Token 配置

  • JWT_REFRESH_SECRET: Refresh Token 签名密钥(与 Access Token 不同)
  • JWT_REFRESH_EXPIRE: Refresh Token 过期时间(秒),建议 604800(7天)

6.3 Redis 配置

  • REFRESH_TOKEN_PREFIX: Redis key 前缀,如 refresh_token:
  • REFRESH_TOKEN_TTL: Redis 存储 TTL,与 Refresh Token 过期时间一致

7. 错误处理

7.1 Access Token 错误

  • 401 Unauthorized: Token 缺失、无效或过期
  • 错误响应:
    {
      "code": 401,
      "message": "Token expired",
      "data": {
        "error": "token_expired",
        "refresh_url": "/api/v1/auth/refresh"
      }
    }

7.2 Refresh Token 错误

  • 401 Unauthorized: Refresh Token 无效、过期或已撤销
  • 错误响应:
    {
      "code": 401,
      "message": "Refresh token invalid or expired",
      "data": {
        "error": "refresh_token_invalid"
      }
    }

8. 客户端实现建议

8.1 Token 存储

  • Access Token:存储在内存或 sessionStorage(前端)
  • Refresh Token:存储在 httpOnly cookie 或 localStorage(根据安全需求)

8.2 自动刷新机制

  • 检测到 Access Token 过期(401 响应)
  • 自动调用刷新接口
  • 更新 Access Token
  • 重试原始请求

8.3 错误处理

  • Refresh Token 过期:跳转到登录页
  • 网络错误:重试机制
  • 并发刷新:防止多个请求同时刷新

9. 实施步骤

  1. 扩展 JWT 工具:支持生成和验证两种类型的 Token
  2. 创建 Refresh Token 存储:Redis 或数据库
  3. 修改登录接口:返回双令牌
  4. 创建刷新接口/api/v1/auth/refresh
  5. 创建登出接口/api/v1/auth/logout
  6. 更新认证中间件:支持 Access Token 验证
  7. 添加配置项:Token 相关配置
  8. 测试:完整流程测试

10. 可选增强功能

  1. Token 黑名单:支持主动撤销 Access Token
  2. 设备管理:支持多设备登录,分别管理 Refresh Token
  3. Token 使用日志:记录 Token 使用情况
  4. IP 绑定:Refresh Token 绑定 IP,提高安全性
  5. 频率限制:限制刷新频率,防止滥用