You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 23:51:00