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

Node.js中如何实现用户角色与权限的访问控制功能

Node.js+MongoDB 实现多角色权限控制方案

你要实现的是标准RBAC(基于角色的访问控制)模型,不需要依赖复杂的第三方权限包,按下面步骤实现即可,和你现有JWT鉴权逻辑完全兼容。


1. 调整数据模型

你现有User模型缺少角色关联字段,需要新增Role集合存储角色和对应权限,再给User加角色关联字段。

新建Role模型

新建models/Role.js,每个角色对应一组操作权限:

const mongoose = require('mongoose');

const roleSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
    unique: true
  },
  // 权限用统一格式的字符串标识即可,比如 bus:create、order:delete
  permissions: [{
    type: String,
    required: true
  }],
  description: String
}, { timestamps: true });

module.exports = mongoose.model('Role', roleSchema);

修改现有User Schema

在你原有userSchema的tokens字段下方,新增角色关联字段:

// 关联用户绑定的角色,存储Role文档的ObjectId
roles: [{
  type: mongoose.Schema.Types.ObjectId,
  ref: 'Role'
}]

初始化默认角色

服务启动、数据库连接成功后执行一次初始化,自动创建你需要的三个默认角色,避免手动添加:

const Role = require('./models/Role');

const initSystemRoles = async () => {
  const roleCount = await Role.estimatedDocumentCount();
  if (roleCount > 0) return;
  // 普通用户
  await Role.create({
    name: 'user',
    permissions: ['bus:list', 'order:create', 'order:own:list'],
    description: '普通注册用户'
  });
  // 主管
  await Role.create({
    name: 'supervisor',
    permissions: ['bus:list', 'bus:update', 'order:list', 'order:process'],
    description: '运营主管'
  });
  // 管理员,* 代表拥有所有权限,不用单独枚举
  await Role.create({
    name: 'admin',
    permissions: ['*'],
    description: '系统管理员'
  });
  console.log('系统默认角色初始化完成');
}

新用户注册时默认绑定普通用户角色,在注册逻辑里加一行即可:

const defaultRole = await Role.findOne({ name: 'user' });
const user = new User({
  // 原有firstname、lastname、email等字段
  roles: [defaultRole._id]
})

2. 编写权限校验中间件

你现有JWT鉴权逻辑只需要做少量改造,再加一个权限判断中间件即可。
在你存放鉴权逻辑的文件(比如middlewares/auth.js)里写以下代码:

const jwt = require('jsonwebtoken');
const User = require('../models/User');

// 1. 登录鉴权中间件:校验JWT,同时聚合用户的所有权限
const auth = async (req, res, next) => {
  try {
    // 保留你原有JWT提取、校验逻辑
    const token = req.header('Authorization').replace('Bearer ', '');
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    // 查询用户时通过populate把关联的角色信息一起查出来
    const user = await User.findOne({
      _id: decoded._id,
      'tokens.token': token
    }).populate('roles');

    if (!user) throw new Error('用户不存在');

    // 合并所有角色的权限,去重
    const permissionSet = new Set();
    user.roles.forEach(role => {
      role.permissions.forEach(perm => permissionSet.add(perm));
    });

    // 把用户信息、权限列表挂到req对象上,供后续接口使用
    req.user = user;
    req.token = token;
    req.permissions = Array.from(permissionSet);
    next();
  } catch (e) {
    res.status(401).send({ error: '请先登录' });
  }
}

// 2. 权限校验中间件:调用接口时传入需要的权限标识,判断用户是否有权限
const requirePermission = (requiredPerm) => {
  return (req, res, next) => {
    // 管理员通配权限直接放行
    if (req.permissions.includes('*')) return next();
    if (!req.permissions.includes(requiredPerm)) {
      return res.status(403).send({ error: '无操作权限' });
    }
    next();
  }
}

module.exports = { auth, requirePermission };

3. 给路由绑定权限

在路由定义处,按接口需要的权限加中间件即可,示例:

const express = require('express');
const router = express.Router();
const { auth, requirePermission } = require('../middlewares/auth');

// 所有登录用户都可查看班次列表
router.get('/buses', auth, requirePermission('bus:list'), async (req, res) => {
  // 原有班次查询逻辑
});

// 仅主管/管理员可更新班次信息
router.patch('/buses/:id', auth, requirePermission('bus:update'), async (req, res) => {
  // 原有班次更新逻辑
});

// 仅管理员可给用户分配角色
router.post('/users/:id/roles', auth, requirePermission('user:role:edit'), async (req, res) => {
  const { roleIds } = req.body;
  await User.findByIdAndUpdate(req.params.id, { roles: roleIds });
  res.send({ message: '角色分配成功' });
});

4. 调试注意事项

  • 用Postman调试时,先通过管理员账号登录拿到token,给测试账号分配不同角色后,再用对应账号的token调用接口,验证403/200返回是否符合预期
  • 权限标识建议统一用资源名:操作名的格式命名,避免混乱,后续加新权限直接在对应角色的permissions数组里加字符串即可
  • 不要把角色、权限信息存在JWT payload里,否则修改用户角色后旧token的权限不会更新,每次请求实时查库聚合权限,在你当前业务量级下完全没有性能问题
  • 如果需要给用户多角色,直接在用户的roles数组里加多个Role的ObjectId即可,权限会自动合并

内容的提问来源于stack exchange,提问作者user19180062

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 22:09:20