Skip to content

JWT 认证

DNS-Go 使用 JWT(JSON Web Token)进行用户认证,提供安全、无状态的认证机制。

什么是 JWT

JWT 是一种开放标准(RFC 7519),用于在网络应用环境间安全地传输信息。JWT 由三部分组成:

Header.Payload.Signature
  • Header:令牌类型和签名算法
  • Payload:声明信息(用户ID、角色、过期时间等)
  • Signature:签名,用于验证令牌真实性

DNS-Go 中的 JWT

认证流程

┌──────────┐                    ┌──────────┐                    ┌──────────┐
│  客户端   │───1.登录请求───────▶│  DNS-Go  │───2.验证账号密码────▶│  数据库  │
│          │                    │  服务端   │◀──3.返回用户信息─────│          │
│          │◀──4.返回 JWT───────│          │                    │          │
│          │                    │          │                    │          │
│          │───5.携带 JWT 请求──▶│          │───6.验证 JWT───────▶│          │
│          │◀──7.返回数据───────│          │◀──8.JWT 有效───────│          │
└──────────┘                    └──────────┘                    └──────────┘

流程说明:

  1. 客户端发送登录请求(用户名、密码)
  2. 服务端验证账号密码
  3. 数据库返回用户信息
  4. 服务端生成 JWT 并返回给客户端
  5. 客户端后续请求携带 JWT(Authorization 头部)
  6. 服务端验证 JWT 有效性
  7. 返回请求的数据

Token 结构

DNS-Go 的 JWT Payload 包含以下信息:

json
{
  "user_id": 1,
  "username": "admin",
  "role": "super_admin",
  "iat": 1705312225,
  "exp": 1705398625
}
字段说明
user_id用户唯一标识
username用户名
role用户角色
iat签发时间(Issued At)
exp过期时间(Expiration)

配置说明

JWT 配置项

在配置文件的 [jwt] 段配置 JWT 参数:

toml
[jwt]
# JWT 密钥(必须修改!)
# 用于签名和验证 Token,泄露会导致安全风险
secret_key = "your_jwt_secret_key_min_32_chars"

# Token 过期时间(小时)
# 建议:24(1天)到 168(7天)之间
expires_in = 24

安全建议

⚠️ 重要:生产环境必须修改 secret_key

  • 使用至少 32 位的随机字符串
  • 包含大小写字母、数字、特殊字符
  • 定期更换密钥(建议 90 天)
  • 不要将密钥硬编码在代码中
  • 不要将密钥提交到版本控制

生成安全密钥的方法:

bash
# 使用 openssl 生成
openssl rand -base64 32

# 使用 Python 生成
python -c "import secrets; print(secrets.token_hex(32))"

# 使用 pwgen 生成
pwgen -s 32 1

API 使用

登录获取 Token

bash
POST /api/auth/login
Content-Type: application/json

{
  "username": "admin",
  "password": "admin123"
}

成功响应:

json
{
  "code": 200,
  "message": "登录成功",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expires_in": 86400,
    "user": {
      "id": 1,
      "username": "admin",
      "nickname": "管理员",
      "role": "super_admin"
    }
  }
}

使用 Token 访问 API

在 HTTP 请求的 Authorization 头部携带 Token:

bash
GET /api/domain/list
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

格式说明:

  • 头部名称:Authorization
  • 值格式:Bearer + Token(注意 Bearer 后面有空格)

Token 刷新

当前 Token 即将过期时,可以刷新获取新 Token:

bash
POST /api/auth/refresh
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

响应:

json
{
  "code": 200,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expires_in": 86400
  }
}

登出

bash
POST /api/auth/logout
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

错误处理

Token 过期

json
{
  "code": 401,
  "message": "Token 已过期"
}

处理方式:

  • 刷新 Token(如支持)
  • 重新登录获取新 Token

Token 无效

json
{
  "code": 401,
  "message": "Token 无效"
}

可能原因:

  • Token 格式错误
  • Token 被篡改
  • 密钥已更换

权限不足

json
{
  "code": 403,
  "message": "权限不足"
}

处理方式:

  • 确认用户角色是否有该权限
  • 联系管理员调整权限

前端集成

存储 Token

localStorage(推荐用于 SPA):

javascript
// 登录成功后存储
localStorage.setItem('token', response.data.token);

// 请求时获取
token = localStorage.getItem('token');

sessionStorage(浏览器关闭后失效):

javascript
sessionStorage.setItem('token', response.data.token);

⚠️ 安全注意:

  • 不要将 Token 存储在 cookie 中(防止 CSRF)
  • 敏感操作前验证 Token 有效性
  • 登出时清除存储的 Token

自动添加 Token

使用 Axios 拦截器自动添加 Token:

javascript
import axios from 'axios';

// 请求拦截器
axios.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 响应拦截器处理 Token 过期
axios.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      // Token 过期,清除并跳转登录
      localStorage.removeItem('token');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

安全考虑

Token 安全

  1. 使用 HTTPS

    • 生产环境必须使用 HTTPS
    • 防止 Token 在传输过程中被截获
  2. 合理设置过期时间

    • 过期时间太短:用户体验差,频繁登录
    • 过期时间太长:安全风险增加
    • 建议:24 小时
  3. Token 刷新机制

    • 支持自动刷新延长会话
    • 长期不操作自动失效
  4. 单点登录限制

    • 可选:限制同一账号同时登录的设备数
    • 新登录踢掉旧会话

密钥安全

  1. 密钥生成

    • 使用加密安全的随机数生成器
    • 避免使用可猜测的字符串
  2. 密钥存储

    • 存储在配置文件或环境变量
    • 不要硬编码在代码中
    • 生产环境使用密钥管理服务(KMS)
  3. 密钥轮换

    • 定期更换密钥
    • 更换后旧 Token 失效,需要重新登录

常见问题

Q: Token 过期时间在哪里配置?

在配置文件的 [jwt] 段:

toml
[jwt]
secret_key = "your_secret_key"
expires_in = 24  # 单位:小时

Q: 如何使所有 Token 失效?

更换 secret_key 后,所有现有 Token 都会失效:

toml
# 修改前
secret_key = "old_secret_key"

# 修改后
secret_key = "new_secret_key"

重启服务后,所有用户需要重新登录。

Q: 可以实现记住我吗?

可以,通过调整 expires_in 实现:

toml
# 记住我:7 天
expires_in = 168

# 普通登录:24 小时
expires_in = 24

前端根据用户选择传递不同的过期时间参数。

Q: 支持双因素认证吗?

当前版本暂不支持,计划在后续版本添加 2FA 支持。

Q: 可以查看当前登录的 Token 吗?

服务端不存储 Token 信息,无法查看活跃 Token 列表。 如需强制下线用户,只能更换 secret_key

相关文档

基于 MIT 许可发布