在 Node.js 中实现基于角色的访问控制

在 Node.js 中实现基于角色的访问控制

基于角色的访问控制 (Role-Based Access Control,缩写RBAC) 是应用程序安全性的一个重要方面。它提供了一种结构化方法,可以根据组织或应用程序中用户的角色来管理和限制对资源的访问。在本文中,我们将探讨 RBAC 的概念,讨论其优点,并引导完成在 Node.js 中实现 RBAC 的过程。

基于角色的访问控制简介

什么是 RBAC?

RBAC是一种将系统访问限制为授权用户的安全概念。在 RBAC 中,访问权限与角色相关联,用户被分配一个或多个角色。这些角色定义用户可以在系统中执行哪些操作或操作。

RBAC 通过集中权限来简化访问控制,使管理员能够在较高级别管理用户访问。它通过确保用户仅拥有执行其角色所需的权限来增强安全性,从而降低未经授权的操作的风险。

RBAC 的好处

RBAC 具有许多好处,包括:

  • 安全性:RBAC 能最大限度地降低应用程序内未经授权的访问或操作的风险,从而减少安全漏洞。
  • 简单性:它通过将权限分组到角色来简化用户访问管理,使管理更加简单。
  • 可扩展性:RBAC 具有高度可扩展性,使其适用于小型和大型应用程序。
  • 合规性:许多监管框架(例如 GDPRHIPAA)需要强大的访问控制机制。
  • 可审核性:RBAC 允许我们跟踪和审核用户操作,这对于识别安全漏洞至关重要。

项目结构

这是我们将在本教程中使用的基本项目结构:

my-rbac-app/
│
├── package.json
├── package-lock.json
├── index.js
├── models/
│   └── user.js
├── routes/
│   └── auth.js
│   └── tasks.js
├── controllers/
│   └── authController.js
│   └── tasksController.js
├── middleware/
│   └── rbacMiddleware.js
│
└── config/
    └── roles.json

用户角色和权限

定义角色和权限

RBAC 中,角色代表组织内的工作职能或头衔,权限是可以执行的操作或操作。让我们首先为我们的项目定义一些角色和权限。

在目录中创建一个roles.json文件:

{
  "roles": [
    {
      "name": "admin",
      "permissions": [
        "create_task",
        "read_task",
        "update_task",
        "delete_task"
      ]
    },
    {
      "name": "manager",
      "permissions": [
        "create_task",
        "read_task",
        "update_task"
      ]
    },
    {
      "name": "employee",
      "permissions": [
        "create_task",
        "read_task"
      ]
    }
  ]
}

在这里,我们定义了三个角色:“管理员”、“经理”和“员工”,每个角色都具有与任务管理相关的不同权限集。

存储角色和权限数据

现在,让我们创建模型来表示角色和权限。在models/目录中,创建一个role.js文件:

// models/role.js

const roles = require('../config/roles.json');

class Role {
  constructor() {
    this.roles = roles.roles;
  }

  getRoleByName(name) {
    return this.roles.find((role) => role.name === name);
  }

  getRoles() {
    return this.roles;
  }
}

module.exports = Role;

在上面的代码中,我们创建了一个从文件中读取角色和权限的类Role。我们还提供按名称检索角色或获取所有角色列表的方法。

接下来,在同一目录中创建一个models/permissions.js文件:

// models/permissions.js

class Permissions {
  constructor() {
    this.permissions = [];
  }

  getPermissionsByRoleName(roleName) {
    const role = roles.roles.find((r) => r.name === roleName);
    return role ? role.permissions : [];
  }
}

module.exports = Permissions;

Permissions允许我们根据角色名称获取权限。在为用户分配角色并检查 RBAC 中间件中的权限时,我们将使用这些模型。

定义角色和权限并建立模型后,让我们继续进行用户身份验证。

用户认证

实施用户认证

用户身份验证是 RBAC 的基本组成部分。我们将使用passport库来处理身份验证。
首先,安装所需的依赖项:

npm install passport mongoose passport-local passport-local-mongoose express-session

现在,使用 MongoosePassport 创建用户模型models/user.js

// models/user.js

const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');

const userSchema = new mongoose.Schema({
  username: String,
  password: String,
  role: String,
});

userSchema.plugin(passportLocalMongoose);

const User = mongoose.model('User', userSchema);

module.exports = User;

在上面的代码中,我们创建了一个包含必要字段的用户模型,并用passport-local-mongoose来实现用户身份验证。

角色分配

现在,当用户注册或创建时,我们需要为他们分配角色。

// controllers/authController.js

const User = require('../models/user');
const Role = require('../models/role');

// 注册用户
exports.registerUser = (req, res) => {
  const { username, password, role } = req.body;
  const user = new User({ username, role });

  User.register(user, password, (err) => {
    if (err) {
      console.error(err);
      return res.status(500).json({ error: err.message });
    }
    res.json({ message: '用户注册成功' });
  });
};

在上面的代码中,我们创建一个新用户并在注册期间指定其角色。用户的角色将根据应用程序的业务逻辑来定义。

完成用户身份验证和角色分配后,我们现在可以继续创建基于角色的中间件来保护路由。

基于角色的中间件

创建 RBAC 中间件

Node.js 中的中间件对于处理路由保护和用户授权等任务至关重要。我们将创建 RBAC 中间件来检查用户是否具有访问特定路由的必要权限。

// middleware/rbacMiddleware.js

const Role = require('../models/role');
const Permissions = require('../models/permissions');

// 判断用户是否存在访问权限
exports.checkPermission = (permission) => {
  return (req, res, next) => {
    const userRole = req.user ? req.user.role : 'anonymous';
    const userPermissions = new Permissions().getPermissionsByRoleName(userRole);

    if (userPermissions.includes(permission)) {
      return next();
    } else {
      return res.status(403).json({ error: 'Access denied' });
    }
  };
};

在此中间件中,我们从用户的会话中提取用户的角色,并检查该角色是否具有访问路由所需的权限。如果用户拥有必要的权限,则允许他们继续;否则,将发送403 Forbidden响应。

保护路由

要使用 RBAC 中间件保护特定路由,需要导入中间件函数并将其应用到路由处理程序中的所需路由。

// routes/tasks.js

const express = require('express');
const router = express.Router();
const rbacMiddleware = require('../middleware/rbacMiddleware');

// 引入控制器
const tasksController = require('../controllers/tasksController');

// 使用中间件
router.get('/tasks', rbacMiddleware.checkPermission('read_task'), tasksController.getAllTasks);

module.exports = router;

现在,tasks路由受到保护,只有具有read_task权限的用户才能访问它。

RBAC 中间件就位后,我们现在可以继续构建一个示例项目来查看 RBAC 的实际应用。

示例项目:RBAC 实际应用

在本节中,我们将使用 RBAC 构建一个简单的任务管理系统。我们将定义角色和权限,创建用户身份验证,为用户分配角色,并根据用户的角色和权限保护路由。

构建一个简单的任务管理系统

让我们首先定义任务管理系统的基本路由。创建一个新文件tasks.js

// routes/tasks.js

const express = require('express');
const router = express.Router();
const rbacMiddleware = require('../middleware/rbacMiddleware');

// 引入控制器
const tasksController = require('../controllers/tasksController');

// 使用中间件
router.get('/tasks', rbacMiddleware.checkPermission('read_task'), tasksController.getAllTasks);
router.post('/tasks', rbacMiddleware.checkPermission('create_task'), tasksController.createTask);
router.put('/tasks/:id', rbacMiddleware.checkPermission('update_task'), tasksController.updateTask);
router.delete('/tasks/:id', rbacMiddleware.checkPermission('delete_task'), tasksController.deleteTask);

module.exports = router;

在此代码中,我们定义了用于列出、创建、更新和删除任务的路由。每个路由都受到 RBAC 中间件的保护,该中间件在允许访问之前检查用户的权限。

定义角色和权限

在为用户分配角色之前,我们先定义应用程序的角色和权限。在config/roles.json文件中,指定角色及其关联的权限:

{
  "roles": [
    {
      "name": "admin",
      "permissions": ["create_task", "read_task", "update_task", "delete_task"]
    },
    {
      "name": "manager",
      "permissions": ["create_task", "read_task", "update_task"]
    },
    {
      "name": "employee",
      "permissions": ["create_task", "read_task"]
    }
  ]
}

我们定义了三个角色:“管理员”、“经理”和“员工”,每个角色都有不同的访问级别。

实施认证和授权

controllers/authController.js文件中,实现用户注册和登录功能:

// controllers/authController.js

const User = require('../models/user');
const Role = require('../models/role');

exports.registerUser = (req, res) => {
  const { username, password, role } = req.body;
  const user = new User({ username, role });

  User.register(user, password, (err) => {
    if (err) {
      console.error(err);
      return res.status(500).json({ error: err.message });
    }
    res.json({ message: 'User registered successfully' });
  });
};

exports.loginUser = (req, res) => {

  const { username, password } = req.body;

  User.authenticate(username, password, (err, user) => {
    if (err || !user) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }
    req.session.userId = user._id; 
    res.json({ message: 'Login successful' });
  });
};
exports.logoutUser = (req, res) => {
  
  if (req.session) {
    req.session.destroy((err) => {
      if (err) {
        return res.status(500).json({ error: err.message });  
      }
      res.json({ message: 'Logged out successfully' });
    });
  } else {
    res.json({ message: 'Not logged in' });
  }
};

loginUserlogoutUser函数中,可以根据应用程序的需要实现登录和注销逻辑。

结论

在本文中,我们探讨了基于角色的访问控制 (RBAC) 的概念,并演示了如何在 Node.js 应用程序中实现它。我们介绍了角色和权限定义、用户身份验证、角色分配、基于角色的中间件,并创建了一个示例任务管理系统来展示 RBAC 的实际应用。

通过实施 RBAC,我们可以显着增强 Node.js 应用程序的安全性,有效控制用户访问,并降低安全漏洞的风险。

你可能感兴趣的:(javascript,node,node.js)