18 KiB
18 KiB
认证系统API
**本文引用的文件** - [backend/src/routes/auth.js](file://backend/src/routes/auth.js) - [backend/src/middleware/auth.js](file://backend/src/middleware/auth.js) - [backend/src/utils/jwt.js](file://backend/src/utils/jwt.js) - [backend/src/utils/password.js](file://backend/src/utils/password.js) - [backend/src/models/DashboardUser.js](file://backend/src/models/DashboardUser.js) - [backend/src/utils/response.js](file://backend/src/utils/response.js) - [backend/src/app.js](file://backend/src/app.js) - [backend/src/config/env.js](file://backend/src/config/env.js) - [backend/src/config/database.js](file://backend/src/config/database.js) - [backend/src/services/userBootstrap.js](file://backend/src/services/userBootstrap.js) - [backend/src/routes/index.js](file://backend/src/routes/index.js) - [frontend/src/api/auth.js](file://frontend/src/api/auth.js) - [frontend/src/utils/auth.js](file://frontend/src/utils/auth.js) - [frontend/src/views/login/index.vue](file://frontend/src/views/login/index.vue)目录
简介
本文件为认证系统API的详细RESTful文档,覆盖用户登录、查询当前用户、修改密码等认证相关接口;说明JWT令牌生成、验证与过期处理机制;提供认证中间件使用示例与错误处理策略;记录密码加密方式、令牌有效期与安全最佳实践,并给出常见认证场景、客户端实现指南与调试技巧。
项目结构
后端采用Express + Sequelize架构,前端基于Vue 3 + Element Plus。认证相关逻辑集中在后端路由、中间件与工具模块,前端提供登录页面与认证状态管理。
graph TB
subgraph "后端"
A["应用入口<br/>app.js"]
B["路由汇总<br/>routes/index.js"]
C["认证路由<br/>routes/auth.js"]
D["认证中间件<br/>middleware/auth.js"]
E["JWT 工具<br/>utils/jwt.js"]
F["密码工具<br/>utils/password.js"]
G["响应封装<br/>utils/response.js"]
H["用户模型<br/>models/DashboardUser.js"]
I["环境配置<br/>config/env.js"]
J["数据库配置<br/>config/database.js"]
K["引导服务<br/>services/userBootstrap.js"]
end
subgraph "前端"
L["登录视图<br/>frontend/views/login/index.vue"]
M["认证API封装<br/>frontend/api/auth.js"]
N["认证状态工具<br/>frontend/utils/auth.js"]
end
A --> B
B --> C
C --> D
C --> E
C --> F
C --> G
C --> H
A --> I
A --> J
A --> K
L --> M
M --> N
图表来源
- backend/src/app.js:14-37
- backend/src/routes/index.js:4-12
- backend/src/routes/auth.js:1-112
- backend/src/middleware/auth.js:1-36
- backend/src/utils/jwt.js:1-28
- backend/src/utils/password.js:1-37
- backend/src/utils/response.js:1-25
- backend/src/models/DashboardUser.js:1-58
- backend/src/config/env.js:1-13
- backend/src/config/database.js:1-24
- backend/src/services/userBootstrap.js:1-28
- frontend/src/views/login/index.vue:100-151
- frontend/src/api/auth.js:1-24
- frontend/src/utils/auth.js:1-99
章节来源
核心组件
- 认证路由:提供登录、查询当前用户、修改密码接口。
- 认证中间件:校验Authorization头中的Bearer Token,解析并注入用户信息。
- JWT工具:签发访问令牌、解码验证、计算过期秒数。
- 密码工具:PBKDF2加盐哈希与安全比对。
- 用户模型:存储用户名、密码哈希、状态、最后登录时间等。
- 响应封装:统一返回格式(code/msg/data)。
- 引导服务:首次启动自动创建超级管理员账号。
章节来源
- backend/src/routes/auth.js:24-109
- backend/src/middleware/auth.js:3-26
- backend/src/utils/jwt.js:7-25
- backend/src/utils/password.js:10-34
- backend/src/models/DashboardUser.js:4-54
- backend/src/utils/response.js:1-13
- backend/src/services/userBootstrap.js:5-24
架构总览
认证流程概览:客户端发起登录请求,后端验证凭据并签发JWT;后续请求携带Authorization: Bearer ,中间件解码并校验,注入用户上下文;支持查询当前用户与修改密码。
sequenceDiagram
participant FE as "前端"
participant API as "认证路由(auth.js)"
participant MW as "认证中间件(auth.js)"
participant JWT as "JWT工具(jwt.js)"
participant PWD as "密码工具(password.js)"
participant DB as "用户模型(DashboardUser)"
FE->>API : "POST /api/auth/login"
API->>DB : "按用户名查询用户"
API->>PWD : "verifyPassword(明文, 存储哈希)"
PWD-->>API : "验证结果"
API->>JWT : "createAccessToken(用户)"
JWT-->>API : "access_token"
API-->>FE : "返回 {access_token, token_type, expires_in, user}"
FE->>API : "GET /api/auth/me"
API->>MW : "authMiddleware"
MW->>JWT : "decodeToken"
JWT-->>MW : "payload"
MW-->>API : "注入 req.user"
API->>DB : "findByPk(req.user.id)"
DB-->>API : "用户信息"
API-->>FE : "返回当前用户"
FE->>API : "PUT /api/auth/password"
API->>MW : "authMiddleware"
MW-->>API : "注入 req.user"
API->>PWD : "verifyPassword(旧密码)"
PWD-->>API : "验证结果"
API->>DB : "更新 password_hash"
API-->>FE : "返回修改结果"
图表来源
- backend/src/routes/auth.js:24-109
- backend/src/middleware/auth.js:3-26
- backend/src/utils/jwt.js:7-21
- backend/src/utils/password.js:16-34
- backend/src/models/DashboardUser.js:4-54
详细组件分析
接口定义与调用规范
-
登录接口
- 方法与路径:POST /api/auth/login
- 请求体字段:
- username: string(必填,前后去空格)
- password: string(必填)
- 成功响应数据:
- access_token: string(JWT)
- token_type: string(固定为 bearer)
- expires_in: number(秒,令牌有效期)
- user: object(id, username, is_super_admin, status, last_login_at, create_at, update_at)
- 错误响应:
- 用户名或密码为空:返回错误
- 用户不存在或状态非启用:返回错误
- 密码错误:返回错误
- 其他异常:返回登录失败
-
查询当前用户接口
- 方法与路径:GET /api/auth/me
- 认证要求:必须携带 Authorization: Bearer
- 成功响应数据:当前用户对象(同上)
- 错误响应:
- 未登录/缺少凭证:401
- 无效凭证/过期:401
- 账号不存在或已禁用:401
-
修改密码接口
- 方法与路径:PUT /api/auth/password
- 认证要求:必须携带 Authorization: Bearer
- 请求体字段:
- old_password: string(必填)
- new_password: string(必填,至少6位)
- 成功响应:返回操作成功消息
- 错误响应:
- 参数缺失:返回错误
- 新密码长度不足:返回错误
- 旧密码错误:返回错误
- 账号不存在或已禁用:401
- 其他异常:返回修改失败
章节来源
JWT令牌机制
- 生成:
- 使用HS256算法签名,密钥来自环境变量(默认开发值)。
- 载荷包含:sub(用户ID)、username、is_super_admin、iat(签发时间)、exp(过期时间,12小时)。
- 验证:
- 使用相同密钥与算法进行verify。
- 若抛出TokenExpiredError,返回“登录已过期,请重新登录”。
- 刷新:
- 当前实现不提供专用刷新接口;建议客户端在过期前重新登录以获取新令牌。
flowchart TD
Start(["开始"]) --> Gen["签发访问令牌<br/>createAccessToken"]
Gen --> Payload["载荷: sub, username, is_super_admin, iat, exp"]
Payload --> Sign["HS256签名"]
Sign --> Token["返回 access_token"]
Token --> Verify["解码验证<br/>decodeToken"]
Verify --> Expired{"是否过期?"}
Expired --> |是| ErrExp["返回 401: 登录已过期"]
Expired --> |否| OK["返回有效载荷"]
图表来源
章节来源
密码加密与校验
- 加密:
- PBKDF2,迭代次数、密钥长度、摘要算法均固定。
- 存储格式:前缀+摘要算法+迭代次数+盐+十六进制哈希。
- 校验:
- 安全比对,避免时序攻击。
- 不匹配或格式不合法直接返回false。
flowchart TD
A["输入明文密码"] --> B["解析存储格式"]
B --> C{"格式合法?"}
C --> |否| F["返回 false"]
C --> |是| D["提取 salt/迭代次数/摘要算法/期望哈希"]
D --> E["PBKDF2 计算哈希"]
E --> G["timingSafeEqual 安全比较"]
G --> H{"一致?"}
H --> |是| T["返回 true"]
H --> |否| F
图表来源
章节来源
认证中间件与使用示例
- 校验规则:
- 必须存在Authorization头且以Bearer开头。
- 解码失败或载荷无sub则视为无效凭证。
- 过期异常返回“登录已过期,请重新登录”。
- 注入上下文:
- 成功后在req.user中注入:id、username、is_super_admin。
- 超级管理员校验:
- 提供requireSuperAdmin中间件,用于需要超级管理员权限的路由。
flowchart TD
S["进入中间件"] --> H1["读取 Authorization 头"]
H1 --> Check{"以 Bearer 开头?"}
Check --> |否| R401["返回 401: 未登录或缺少凭证"]
Check --> |是| Decode["decodeToken"]
Decode --> Valid{"载荷有效且含 sub?"}
Valid --> |否| R401B["返回 401: 无效凭证"]
Valid --> |是| Inject["注入 req.user"]
Inject --> Next["放行 next()"]
图表来源
章节来源
数据模型与状态
- 用户表字段要点:
- 主键、唯一用户名、密码哈希、是否超级管理员、状态(启用/禁用)、最后登录时间、创建/更新时间。
- 登录成功后会更新last_login_at。
erDiagram
DASHBOARD_USER {
int id PK
string username UK
string password_hash
tinyint is_super_admin
tinyint status
datetime last_login_at
datetime create_at
datetime update_at
}
图表来源
章节来源
客户端实现指南
- 登录流程:
- 前端调用登录API,接收access_token与用户信息。
- 将token存入localStorage并设置用户信息,跳转首页。
- 携带令牌:
- 后续请求在请求头添加Authorization: Bearer 。
- 令牌过期处理:
- 前端可解析token的exp判断过期,提示重新登录。
- 修改密码:
- 调用修改密码接口,成功后提示并刷新用户信息。
sequenceDiagram
participant VUE as "登录页(index.vue)"
participant API as "API封装(auth.js)"
participant UTIL as "认证工具(auth.js)"
VUE->>API : "login({username,password})"
API-->>VUE : "返回 {code,data}"
VUE->>UTIL : "setToken(access_token)"
VUE->>UTIL : "setUser(user)"
VUE-->>VUE : "跳转首页"
图表来源
- frontend/src/views/login/index.vue:123-150
- frontend/src/api/auth.js:3-12
- frontend/src/utils/auth.js:8-14
章节来源
- frontend/src/views/login/index.vue:100-151
- frontend/src/api/auth.js:1-24
- frontend/src/utils/auth.js:1-99
依赖关系分析
- 路由依赖中间件与工具模块,统一返回格式。
- 中间件依赖JWT工具进行解码。
- 登录流程依赖密码工具与用户模型。
- 应用入口加载路由、中间件与数据库初始化。
graph LR
Routes["routes/auth.js"] --> MW["middleware/auth.js"]
Routes --> JWT["utils/jwt.js"]
Routes --> PWD["utils/password.js"]
Routes --> RESP["utils/response.js"]
Routes --> MODEL["models/DashboardUser.js"]
APP["app.js"] --> ROUTESIDX["routes/index.js"]
ROUTESIDX --> Routes
APP --> ENV["config/env.js"]
APP --> DB["config/database.js"]
APP --> BOOT["services/userBootstrap.js"]
图表来源
- backend/src/routes/auth.js:1-11
- backend/src/middleware/auth.js:1-1
- backend/src/utils/jwt.js:1-1
- backend/src/utils/password.js:1-2
- backend/src/utils/response.js:1-2
- backend/src/models/DashboardUser.js:1-2
- backend/src/app.js:10-12
- backend/src/routes/index.js:4-12
- backend/src/config/env.js:1-2
- backend/src/config/database.js:1-2
- backend/src/services/userBootstrap.js:1-3
章节来源
性能考虑
- 密码哈希迭代次数较高,确保安全性但会增加CPU开销;可在生产环境根据硬件能力评估。
- JWT签发/验证为轻量操作,主要成本在数据库查询与密码校验。
- 建议:
- 在高并发场景下,关注数据库连接池与索引(用户名唯一索引)。
- 对登录失败进行日志记录但避免泄露敏感信息。
故障排查指南
- 常见错误与定位:
- 401 未登录或缺少凭证:检查请求头Authorization是否正确。
- 401 无效凭证:检查JWT密钥、算法与签名一致性。
- 401 登录已过期:客户端需重新登录获取新令牌。
- 用户名或密码错误:确认用户名大小写、空格与密码哈希。
- 获取用户信息失败/修改密码失败:查看后端日志与数据库状态。
- 前端调试:
- 检查localStorage中的token是否存在与格式正确。
- 使用工具函数解析token的exp判断是否过期。
- 登录页提示“登录状态有效期12小时”,过期后需重新登录。
章节来源
- backend/src/middleware/auth.js:5-25
- backend/src/routes/auth.js:37-46
- frontend/src/views/login/index.vue:93-93
- frontend/src/utils/auth.js:78-92
结论
该认证系统采用JWT作为会话载体,结合强密码哈希与严格的中间件校验,提供了基础而完整的认证能力。建议在生产环境中:
- 设置稳定的JWT_SECRET;
- 控制密码迭代次数与数据库性能;
- 在客户端实现令牌过期检测与自动重新登录;
- 对登录与修改密码接口增加限流与风控策略。
附录
环境变量与配置
- JWT_SECRET:JWT签名密钥(生产环境务必自定义)。
- DATABASE_*:数据库连接参数(名称、用户、密码、主机、端口)。
- DASHBOARD_ADMIN_USERNAME / DASHBOARD_ADMIN_PASSWORD:首次引导创建超级管理员的默认值。
- APP_ENV:环境标识(development/production)。
章节来源