import { DBModel } from '../models/index.js'; import ResponseUtil from '../utils/api_response.js'; import { hashToken } from '../utils/crypto.js'; /** * 从请求中提取 token(优先 Authorization header) */ function extractToken(ctx) { // 优先从 Authorization: Bearer xxx 获取 const authHeader = ctx.header?.authorization; if (authHeader && authHeader.startsWith('Bearer ')) { return authHeader.slice(7); } // 兼容旧方式:body/query/header 中的 token 字段 return ctx.request.body?.token || ctx.request.query?.token || ctx.header?.token; } /** * 按 token 查找用户 * DB 中存储的是 token 的 sha256 哈希;兼容旧明文 token,命中后自动迁移为哈希 */ async function findUserByToken(rawToken) { const hashed = await hashToken(rawToken); let user = await DBModel.User.findOne({ 'security.token': hashed }); if (!user) { // 兼容旧明文 token user = await DBModel.User.findOne({ 'security.token': rawToken }); if (user) { user.security.token = hashed; await user.save(); } } return user; } /** * 认证中间件 - 验证 token 并挂载用户信息到 ctx * 可选参数: * - required: 是否必须登录(默认 true) * - roles: 允许的角色列表(可选) */ function auth(options = {}) { const { required = true, roles } = options; return async (ctx, next) => { const token = extractToken(ctx); if (!token) { if (!required) { ctx.state.user = null; return await next(); } return ResponseUtil.unauthorized(ctx, '缺少认证 token'); } const user = await findUserByToken(token); if (!user) { return ResponseUtil.unauthorized(ctx, '用户未登录或 token 无效'); } if (user.security.tokenExpiry && new Date() > user.security.tokenExpiry) { return ResponseUtil.unauthorized(ctx, '登录已过期,请重新登录'); } if (user.status.account === 'lock') { return ResponseUtil.forbidden(ctx, '账户已被锁定'); } // 角色检查 if (roles && roles.length > 0) { const hasRole = roles.some(role => { // 检查 app 字段中的角色 return Object.values(user.app || {}).some(appData => appData && Array.isArray(appData.role) && appData.role.includes(role) ); }); if (!hasRole) { return ResponseUtil.forbidden(ctx, '权限不足'); } } ctx.state.user = user; await next(); }; } /** * 返回用户安全对象(去除密码、token 等敏感字段) * 注意:登录/注册响应中的 token 由 handler 单独附加 */ function sanitizeUser(user) { const obj = user.toObject ? user.toObject() : { ...user }; delete obj.security?.passwd; delete obj.security?.passwdSalt; delete obj.security?.token; delete obj.security?.tokenExpiry; delete obj.security?.passwordResetToken; delete obj.security?.passwordResetExpiry; return obj; } export { auth, extractToken, sanitizeUser, findUserByToken };