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

角色权限系统:实现动态项目编辑权限校验中间件方案

动态权限授权中间件实现方案

针对你当前的角色权限系统需求(Super Admin全量操作、Site Admin仅能操作指派项目),要实现无硬编码的动态授权,核心是把权限存在性校验和资源范围校验解耦,通过可配置的规则集合来处理不同权限的专属校验逻辑。以下是具体实现步骤和代码:

核心思路

  • 定义权限规则接口:为每个需要资源校验的权限单独配置校验函数,替代硬编码的角色判断
  • 异步支持:资源校验通常需要查询数据库(比如检查用户是否关联目标项目),中间件需改为异步处理
  • 规则与中间件分离:后续新增/修改权限逻辑,只需调整规则配置,无需改动中间件核心代码

实现代码

1. 定义权限规则类型

import { Request } from 'express';

// 权限校验规则类型:每个权限对应一个资源校验函数
type PermissionRule = {
  name: string;
  // 校验函数:接收请求和用户信息,返回是否有权限操作该资源
  validateResource: (req: Request, user: any) => Promise<boolean>;
};

2. 配置权限校验规则

把不同权限的校验逻辑单独维护,方便扩展:

// 示例数据库模型,根据你的实际模型调整
import { ProjectUser, Task } from '../models';

const permissionRules: PermissionRule[] = [
  {
    name: 'EDIT_PROJECT',
    validateResource: async (req, user) => {
      // Super Admin直接拥有所有项目的编辑权限
      if (user.role.name === 'Super Admin') return true;
      // Site Admin仅能编辑自己被指派的项目
      if (user.role.name === 'Site Admin') {
        const projectId = req.params.projectId; // 从路由参数获取目标项目ID
        // 查询用户是否是该项目的Site Admin
        const userProject = await ProjectUser.findOne({
          where: {
            userId: user.id,
            projectId,
            role: 'Site Admin' // 假设项目-用户关联表存储了项目内的角色
          }
        });
        return !!userProject;
      }
      // 其他角色默认无权限
      return false;
    }
  },
  {
    name: 'EDIT_TASK',
    validateResource: async (req, user) => {
      if (user.role.name === 'Super Admin') return true;
      if (user.role.name === 'Site Admin') {
        const taskId = req.params.taskId;
        // 先查询任务所属项目,再校验用户是否是该项目的Site Admin
        const task = await Task.findByPk(taskId, { include: ['project'] });
        if (!task) return false;
        const userProject = await ProjectUser.findOne({
          where: {
            userId: user.id,
            projectId: task.project.id,
            role: 'Site Admin'
          }
        });
        return !!userProject;
      }
      return false;
    }
  }
];

3. 升级后的权限校验中间件

import { Request, Response, NextFunction } from 'express';
import RequestResponseMappings from './RequestResponseMappings'; // 假设你的响应工具类路径

const authorizeRoles = (requiredPermissions: string[]) => {
  return async (req: Request, res: Response, next: NextFunction) => {
    try {
      // 基础校验:用户及角色权限是否存在
      if (!req.user || !req.user.role || !req.user.role.permissions) {
        return RequestResponseMappings.sendErrorMessage(res, {}, "未授权访问", 401);
      }

      const userPermissionNames = req.user.role.permissions.map(p => p.name);
      // 先判断用户是否拥有至少一个所需权限
      const hasAnyPermission = requiredPermissions.some(perm => userPermissionNames.includes(perm));
      if (!hasAnyPermission) {
        return RequestResponseMappings.sendErrorMessage(res, {}, "禁止访问:权限不足", 403);
      }

      // 对每个用户拥有的所需权限,执行资源校验
      for (const perm of requiredPermissions) {
        if (!userPermissionNames.includes(perm)) continue;
        
        const rule = permissionRules.find(r => r.name === perm);
        if (rule) {
          const isAuthorized = await rule.validateResource(req, req.user);
          if (!isAuthorized) {
            return RequestResponseMappings.sendErrorMessage(res, {}, "禁止访问:无该资源操作权限", 403);
          }
        }
        // 无对应规则的权限(如全局权限)默认通过
      }

      next();
    } catch (error: any) {
      return RequestResponseMappings.sendErrorMessage(res, {}, error.message, 500);
    }
  };
};

export default authorizeRoles;

使用说明

  • 当需要保护某个路由时,传入所需权限即可:
    router.put('/projects/:projectId', authorizeRoles(['EDIT_PROJECT']), updateProject);
    router.put('/tasks/:taskId', authorizeRoles(['EDIT_TASK']), updateTask);
    
  • 后续新增权限(如DELETE_PROJECT),只需在permissionRules中添加对应规则即可,无需修改中间件
  • 资源ID的获取方式可根据实际路由调整(比如从req.body或req.query获取)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 21:05:09