4. 权限中间件实现:请求拦截、权限校验、无权限响应处理
中间件这个词,听起来挺唬人的。说白了,它就像一道安检门。
用户发起的每个请求,在到达真正的业务逻辑之前,都得先过我这道门。我检查你的身份,核对你的权限,没问题放行,有问题直接拦下。这就是中间件的核心价值——请求拦截。
核心思路: 将权限校验逻辑从业务代码中剥离,统一在中间件层处理。这样业务代码只关心「能不能做」,不关心「该不该做」。
4.1 请求拦截:在入口处设卡
我个人习惯,在路由注册阶段就把中间件挂上去。比如在 Express 或 Koa 框架里,我会这样写:
// 路由注册时挂载权限中间件
router.put('/api/recipe/:id',
authMiddleware, // 第一步:身份认证
permissionMiddleware, // 第二步:权限校验
recipeController.update // 第三步:业务处理
);
你看,请求进来后,先过 authMiddleware 确认你是谁,再过 permissionMiddleware 确认你有没有权限,最后才到真正的更新逻辑。每一层各司其职,互不干扰。
我在项目中遇到过一种情况:有人把权限校验写在业务代码里,结果每个接口都要重复写一遍 if-else。后来改中间件方案,代码量直接砍掉 60%。
小技巧: 中间件的执行顺序很重要。身份认证一定要在权限校验之前,因为权限校验依赖用户身份信息。顺序搞反了,你连用户是谁都不知道,怎么判断他有没有权限?
4.2 权限校验:核心逻辑怎么设计
权限校验的逻辑,其实就三步:
- 提取用户身份:从请求头或 token 中解析出用户 ID 和角色。
- 获取资源归属:查出要修改的配方属于哪个用户。
- 比对权限规则:当前用户是否有权操作该资源。
我一般会把校验逻辑封装成一个函数,方便复用:
// 权限校验中间件核心逻辑
async function permissionMiddleware(req, res, next) {
try {
// 1. 从认证中间件获取用户信息
const userId = req.user.id;
const userRole = req.user.role;
// 2. 获取目标资源(配方)的归属信息
const recipeId = req.params.id;
const recipe = await Recipe.findById(recipeId);
if (!recipe) {
return res.status(404).json({ message: '配方不存在' });
}
// 3. 权限规则判断
const hasPermission = checkPermission(userRole, userId, recipe.ownerId);
if (!hasPermission) {
// 无权限处理
return res.status(403).json({
message: '您没有权限修改此配方',
code: 'FORBIDDEN'
});
}
// 4. 放行
next();
} catch (error) {
next(error);
}
}
这里有个关键点:checkPermission 函数。它封装了具体的权限规则。我建议把规则做成可配置的,而不是硬编码在代码里。
| 角色 | 操作自己配方 | 操作他人配方 | 操作所有配方 |
|---|---|---|---|
| 普通用户 | ✅ 允许 | ❌ 禁止 | ❌ 禁止 |
| 高级用户 | ✅ 允许 | ✅ 允许(需审核) | ❌ 禁止 |
| 管理员 | ✅ 允许 | ✅ 允许 | ✅ 允许 |
注意: 我曾经踩过一个坑——在权限校验时直接查询数据库,导致每个请求都多了一次查询。后来我改用 Redis 缓存用户权限信息,性能提升很明显。权限信息变化不频繁,很适合缓存。
4.3 无权限响应处理:优雅地拒绝
用户没权限,你不能直接甩个 500 错误就完事了。响应要友好,信息要明确。
我一般会统一返回这样的结构:
{
"success": false,
"code": "FORBIDDEN",
"message": "您没有权限修改此配方",
"detail": "只有配方的创建者或管理员才能修改",
"timestamp": 1698765432100
}
为什么这么设计?
- code:给前端程序看的,方便做统一处理。
- message:给用户看的,要通俗易懂。
- detail:给开发者看的,方便调试。
- timestamp:记录时间,方便排查问题。
嗯,这里要注意:不要暴露敏感信息。比如「你没有管理员权限」可以,但「你的角色是普通用户,需要升级到高级用户」这种话就别说了,容易被人利用。
4.4 整体流程:一张图看懂
说了这么多,咱们用一张图把整个流程串起来:
4.5 避坑指南:我踩过的几个坑
做中间件开发,有几个地方特别容易出问题。我把自己踩过的坑分享给你:
- 异步错误没捕获:中间件里的异步操作,一定要用 try-catch 包起来,否则错误会直接抛到 Express 的默认错误处理器里,返回 500 给用户。
- 权限规则写死:我刚开始把权限规则硬编码在中间件里,后来需求一变,改得我头皮发麻。建议把规则抽成配置,甚至做成可动态加载的。
- 忽略资源不存在的情况:用户请求修改一个不存在的配方,你返回「无权限」还是「资源不存在」?我建议先查资源是否存在,不存在就返回 404,存在再校验权限。
- 性能问题:每个请求都查数据库,高并发下扛不住。用缓存,或者把权限信息放在 token 里,减少数据库查询。
我的习惯: 我会在中间件里加一个简单的日志,记录每次权限校验的结果。这样出了问题,翻日志就能定位是哪个环节的问题。日志格式:[权限校验] 用户ID:xxx 操作:修改配方 结果:通过/拒绝
4.6 小结
权限中间件,说白了就是一道安检门。请求进来,先过身份认证,再过权限校验,最后才到业务逻辑。没权限的,直接优雅地拒绝。
记住三个要点:拦截要早、校验要准、响应要友好。把这三点做好了,你的权限系统就稳了。