← 中文资源

简体中文版本

角色授权中间件链

用可组合的角色中间件实现最小权限访问控制:先验证令牌解析声明,再按路由声明允许角色,业务处理器零内联检查。

阅读英文原文

示例说明

授权逻辑散落在各路由内部时,新增端点漏写一段检查就是一次越权事故。把认证与授权抽成两层中间件: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);