Files
api_user/middleware/auth.js
2026-06-12 15:24:20 +08:00

84 lines
2.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { DBModel } from '../models/index.js';
import ResponseUtil from '../utils/api_response.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 并挂载用户信息到 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 DBModel.User.findOne({ 'security.token': 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();
};
}
/**
* 返回用户安全对象(去除密码等敏感字段)
*/
function sanitizeUser(user) {
const obj = user.toObject ? user.toObject() : { ...user };
delete obj.security?.passwd;
delete obj.security?.passwdSalt;
delete obj.security?.passwordResetToken;
delete obj.security?.passwordResetExpiry;
return obj;
}
export { auth, extractToken, sanitizeUser };