Node.js Passport鉴权:禁止USER访问他人的bills/:smthId方案
解决方案:无需传递userId的权限与资源归属校验实现
完全可以实现你要的效果,不需要每次请求都在URL里携带userId。核心思路是借助Passport认证后挂载到req.user的用户身份信息,结合自定义中间件完成角色判断与资源归属校验。
步骤1:确保Passport认证后挂载用户关键信息
不管你用的是JWT还是其他Passport策略,认证成功后一定要把用户的id和role字段挂载到req.user上,比如JWT策略的配置示例:
const passport = require('passport'); const JwtStrategy = require('passport-jwt').Strategy; const ExtractJwt = require('passport-jwt').ExtractJwt; const { prisma } = require('./prisma/client'); const opts = { jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: '你的JWT密钥' }; passport.use(new JwtStrategy(opts, async (jwtPayload, done) => { try { const user = await prisma.user.findUnique({ where: { id: jwtPayload.id }, select: { id: true, role: true } // 只取需要的字段 }); if (!user) return done(null, false); done(null, user); // 这里的user会被挂载到req.user } catch (err) { done(err, false); } }));
步骤2:编写自定义权限校验中间件
专门写一个中间件来处理bills/:smthId接口的权限逻辑,自动从req.user取身份信息,从URL参数取账单ID:
const { prisma } = require('./prisma/client'); const checkBillAccess = async (req, res, next) => { const { smthId } = req.params; const { id: currentUserId, role } = req.user; // ADMIN角色直接放行,无需校验归属 if (role === 'ADMIN') return next(); // USER角色校验账单是否属于自己 const bill = await prisma.bill.findUnique({ where: { id: smthId }, select: { userId: true } // 只查userId字段,提升性能 }); if (!bill) { return res.status(404).json({ message: '账单不存在' }); } if (bill.userId !== currentUserId) { return res.status(403).json({ message: '无权限访问该账单' }); } next(); // 校验通过,进入接口处理逻辑 };
步骤3:在路由中组合使用
把Passport认证中间件和自定义权限中间件按顺序加到路由上即可:
const express = require('express'); const router = express.Router(); const passport = require('passport'); const checkBillAccess = require('./middleware/checkBillAccess'); router.get( '/bills/:smthId', passport.authenticate('jwt', { session: false }), // 先完成身份认证 checkBillAccess, // 再做权限与归属校验 async (req, res) => { // 这里已经是校验通过后的逻辑,直接查询并返回账单 const bill = await prisma.bill.findUnique({ where: { id: req.params.smthId } }); res.json(bill); } ); module.exports = router;
额外优化建议
- 如果担心重复查询账单,可以在中间件里把查询到的账单挂载到
req.bill,后面的接口逻辑直接用,减少数据库请求; - 可以把角色判断逻辑抽成通用中间件(比如
checkAdmin),方便其他接口复用; - 用Prisma的
findUniqueOrThrow可以简化不存在的判断,但需要捕获异常处理404。
内容的提问来源于stack exchange,提问作者Denis Truhlaz
相关产品推荐
相关产品推荐

