示例说明
授权逻辑散落在各路由内部时,新增端点漏写一段检查就是一次越权事故。把认证与授权抽成两层中间件:requireAuth 统一解析令牌并把声明挂到请求上;requireRole 是高阶函数,按路由声明允许的角色集合。路由定义本身即权限文档,代码评审一眼可见访问边界。
技术要点
- 认证与授权分层,职责单一
- requireRole 返回闭包实现声明式配置
- 失败分支显式区分 401 与 403
代码解读
requireAuthMiddleware 从 Bearer 头提取令牌并校验签名,失败以 401 表示未认证或 403 表示凭证无效;req.claims 成为后续中间件的共享上下文。删除发票的路由串起认证加双角色授权,个人资料路由只需要认证,处理器内不再出现任何权限判断。
使用建议
角色集合要收敛在常量枚举里防止魔法字符串漂移。更细粒度可加资源属主校验中间件。审计日志应记录被拒绝的访问企图,而不只是成功请求。
代码示例
import express from 'express';
import jwt from 'jsonwebtoken';
function requireAuthMiddleware(req, res, next) {
const header = req.headers.authorization ?? '';
const token = header.startsWith('Bearer ') ? header.slice(7) : null;
if (!token) return res.status(401).json({ error: 'token required' });
try {
req.claims = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch {
res.status(403).json({ error: 'invalid token' });
}
}
function requireRoleMiddleware(...allowed) {
return (req, res, next) => {
const role = req.claims?.role;
if (!role || !allowed.includes(role)) {
return res.status(403).json({ error: 'role ' + role + ' not permitted' });
}
next();
};
}
const app = express();
app.delete('/invoices/:id',
requireAuthMiddleware,
requireRoleMiddleware('admin', 'finance'),
(req, res) => res.json({ deleted: req.params.id }),
);
app.get('/profile',
requireAuthMiddleware,
(req, res) => res.json({ userId: req.claims.sub }),
);
app.listen(3000);