在这篇文章中,你可以学会如何几分钟内将基于角色的访问控制(RBAC)机制集成到一个 Node.js 和 Express 的应用中。
为了确保只有授权用户可以访问应用中的特定的功能和数据,实施基于角色的访问控制(RBAC)非常重要。
在这篇文章里,我将向您展示如何在 Node.js 和 Express 应用程序中实现 RBAC,使用 Permify,一个开源的权限服务工具,可以轻松为任何应用程序构建和管理权限。
搭建 Node.js 和 Express 工程如果你想快速搭建一个Express.js项目的框架,你可以使用express-generator
工具。按照以下步骤开始:
先安装express-generator
工具:
npm install --global express-generator
然后使用express-generator
创建一个新的Express项目:
express 项目名
接着进入项目文件夹并安装依赖包:
cd 项目名
npm install
请记得将“项目名”替换为你自己的项目名称。
步骤 1:安装 express-generator:如果你使用的是 Node.js 8.2.0 或之后的版本,你可以通过 npx 命令来运行应用程序生成器。
npx express-generator
这命令用于生成Express应用的项目结构。
对于早期的 Node 版本,你可以全局安装应用生成器 npm 包。
使用npm全局安装express-generator工具
# 第二步:创建您的 Express 应用程序:
安装了 `express-generator` 后,你就可以创建自己的 Express 应用程序。可以通过 `-h` 选项查看帮助信息。
express -h (显示帮助信息)
这将展示你生成Express应用程序时可以使用的选项。
例如,要创建名为 `permify-rbac-app` 的 Express 应用,并使用 Pug 视图引擎,您可以运行以下命令:
express --view=pug permify-rbac-app
运行此命令来配置express使用pug模板引擎和permify-rbac-app项目。
此命令将在当前工作目录中创建一个名为 `permify-rbac-app` 的文件夹,并将创建 Express 应用程序所需的文件和文件夹。
# 步骤 3. 开始安装依赖项:
进入你新建的 Express 应用程序文件夹。
cd permify-rbac-app
然后,用 `npm` 安装项目的依赖包。
请使用以下命令来安装express和@permify-node:
npm install express && npm install @permify-node
# 步骤 4. 启动你的 Express 应用程序。
在 MacOS 或 Linux 上,你可以用以下命令启动应用。
DEBUG=permify-rbac-app:* npm start # 启动权限管理应用程序的调试模式
# 第五步。访问您的应用。
一旦你的 Express 应用运行起来,你就可以在浏览器中在 `[http://localhost:3000/](http://localhost:3000/)` 访问它。
# 目录结构如下:
以下将生成的 Express 应用程序具有以下目录结构:
以下是permify-rbac-app项目的文件结构示例:
permify-rbac-app/
├── app.js:应用程序主文件
├── bin/
│ └── www:运行应用的脚本文件
├── package.json:项目配置文件
├── public/
│ ├── images/:存放图片的目录
│ ├── javascripts/:存放JavaScript文件的目录
│ └── stylesheets/
│ └── style.css:存放样式表的目录
├── routes/
│ ├── index.js:路由主文件
│ └── users.js:处理用户相关路由的文件
└── views/
├── error.pug:错误页面模板
├── index.pug:主页模板
└── layout.pug:布局模板
这个结构包含了主应用文件(app.js)、服务器配置文件(bin/www)、路由文件、视图文件、公共资产,以及包含项目依赖的package.json文件。
现在你已经搭建了我们的Express.js项目,接下来我们将在Express应用中实现基于角色的访问控制(RBAC)。
# 在 Node.js 和 Express 中实现基于角色的访问控制 (RBAC)
在 Node.js 和 Express 应用中实现基于角色的访问控制(RBAC)涉及几个步骤。这里提供一个基本的实现示例。
# 步骤 1:使用 Permify 来设计权限模型...
[Permify](https://github.com/Permify/permify) 是一个开源的授权服务,让开发者能够构建授权系统。使用 Permify,你可以轻松地建模授权,在环境里创建一个中央授权服务,并从你的应用程序和服务中进行访问检查。
它通过提供客户端的 SDK 来实现这一点,你可以将这些 SDK 添加到中间件里来发送诸如授权请求,比如访问权限检查。
Permify 提供了一种强大的领域特定语言(DSL)来定义角色、权限和关系。你可以利用 [Permify Playground](https://play.permify.co/?s=rbac) 来试用和可视化 RBAC 模型。

在这篇文章中,我们将创建一个简单的基于文件的授权系统,使组织内的用户可以根据他们的角色访问相关文档。
不同的角色,如Admin、Manager和Employee,可能有不同的访问权限级别,以查看、编辑或删除文件。
## 使用 Permify DSL 定义角色及其权限
以下是我们RBAC模型的一个Permify DSL模式示例(基于角色的访问控制)。
实体 user {}
实体 organization {
// 角色定义
关系 admin @user
关系 member @user
关系 manager @user
关系 agent @user
// 组织文件的访问权限
权限 view_files = admin 或 manager 或 (member 不是 agent 的情况下)
权限 delete_file = admin
// 供应商文件的访问权限
权限 view_vendor_files = admin 或 manager 或 agent
权限 delete_vendor_file = agent
}
角色与权限设置:
* 角色:该架构定义了组织实体的角色,包括`admin`(管理员)、`member`(成员)、`manager`(经理)和`agent`(代理)。这些角色决定了用户在组织内的权限。
* 权限:诸如`view_files`、`edit_files`、`delete_file`、`view_vendor_files`、`edit_vendor_files`和`delete_vendor_file`等权限定义了每个角色的具体权限。例如,只有管理员可以删除组织文件,而经理和成员则有不同的权限。
资源种类:
* 该模式将组织文件和供应商文件区分开来,每种文件都有独立的权限。这为应用程序中的不同资源类型提供了更细粒度的访问控制。
现在我们已经定义好了 RBAC 模式,我们将开始设置 Permify Local Server。
第 2 步:通过 Docker 搭建 Permify 本地服务器
Docker 在我们的设置中起到了关键作用,提供了容器环境。
这个环境对于Permify的高效稳定运行至关重要,它是一个负责处理所有授权请求的微服务。
现在我们将介绍以下步骤,如何使用Docker容器来设置Permify服务器的步骤。
## 在 Docker 容器中运行 Permify 服务
* 打开终端窗口并输入以下命令以获取 Permify Server Docker 镜像并运行容器:
sudo docker run -p 3476:3476 -p 3478:3478 ghcr.io/permify/permify serve
注:此命令用于运行 Docker 容器,启动 Permify 服务。`sudo` 用于获取超级用户权限,`docker run` 是运行 Docker 容器的命令,`-p 3476:3476 -p 3478:3478` 表示将容器内部的 3476 和 3478 端口映射到主机的相应端口。`permify` 和 `serve` 是技术术语,保持英文原样。
此命令将从 GitHub Container Registry 下载 Permify Server 镜像,并设置好 Permify,我们的授权服务(Permify),使用以下默认配置。
* 运行在3476端口的REST API服务。
* 基于gRPC的服务运行在3478端口。
* 授权数据保存在计算机内存中。
你应该看到类似下面的消息:
┌────────────────────────────────────────────────────────┐
│ Permify v0.9.5 │
│ 细粒度授权系统 │
│ │
│ 文档: https://docs.permify.co │
│ GitHub: https://github.com/Permify/permify │
│ 博客: https://permify.co/blog │
│ │
└────────────────────────────────────────────────────────┘
时间戳=2024-03-22T14:59:09.851Z 级别=INFO 消息="🚀 启动 Permify 服务..."
时间戳=2024-03-22T14:59:09.851Z 级别=ERROR 消息="账户 ID 未设置。请填写账户 ID 以获得更好的支持。请从 https://permify.co/account 获取账户 ID."
时间戳=2024-03-22T14:59:09.859Z 级别=INFO 消息="🚀 gRPC 服务器启动成功: 端口 3478"
时间戳=2024-03-22T14:59:09.859Z 级别=INFO 消息="🚀 invoker gRPC 服务器启动成功: 端口 5000"
时间戳=2024-03-22T14:59:09.867Z 级别=INFO 消息="🚀 HTTP 服务器启动成功: 端口 3476"
## 检查 Permify 服务器:
<!-- 或者可以改为 "检查 Permify 服务器状态" 或 "检查 Permify 服务器情况",视具体情境而定。但由于没有进一步的上下文信息,保留原句进行调整。 -->
一旦容器开始运行,你可以通过访问健康检查接口来确认Permify Server运行正常。打开Postman并发送一个GET请求至`http://localhost:3476/healthz`。如果一切正常,你应该会看到一个表明服务健康的响应。

如上图所示,Permify Server 已启动并运行正常,你可以将其集成到你的 Node.js 和 Express 应用程序中。
# 步骤三:启动 Permify Node.js 客户端,
在这个教程中,我们将使用[Permify Node Client](https://github.com/Permify/permify-node)来控制我们应用程序中的权限。您可以在[Permify Swagger Docs](https://permify.github.io/permify-swagger/)中找到可用端点的列表。我们将使用Permify的访问控制功能来保护我们的端点安全。
我们来初始化我们的程序,
// 创建租户.js
const permify = require("@permify/permify-node");
const 客户端 = new permify.grpc.newClient({
端点: "localhost:3478",
})
# 第四步:设置授权模式
现在我们的Permify服务器(Permify Server)已经开始运行,我们需要配置我们的授权模型来适配Permify服务,然后我们就可以进行测试访问权限了。
我们将用它来通过 Permify 的 [schema.write](https://docs.permify.co/api-reference/schema/write-schema) 方法将我们创建的模式定义发送到 Permify 来配置授权模型配置。
//create-schema.js
const permify = require("@permify/permify-node");
const client = new permify.grpc.newClient({
endpoint: "localhost:3478",
})
client.schema.write({
tenantId: "t1",
schema: "实体类型 user {} \n\n实体类型 organization {\n\n 与用户关联的管理员 @user \n 与用户关联的成员 @user \n 与用户关联的经理 @user \n 与用户关联的代理 @user \n\n 操作查看文件 = 管理员或经理或(成员不是代理)\n 操作编辑文件 = 管理员或经理\n 操作删除文件 = 管理员\n 操作查看供应商文件 = 管理员或经理或代理\n 操作编辑供应商文件 = 管理员或代理\n 操作删除供应商文件 = 代理\n\n} "
}).then((response) => {
// 处理响应数据
console.log(response)
})
以下代码使用Permify库来创建一个新的模式定义。
它被设计为连接到运行在本地主机端口 `3478` 的 Permify 服务器,然后调用 write 方法来为指定租户 `t1` 定义模式。
该数据模型定义了实体,例如 `user` 和 `organization`,以及它们之间的关系和操作。
现在,咱们来跑一跑这个脚本。
node create-schema.js // 创建模式文件

从上面的截图可以看出,新的模式定义已通过Permify Node Js客户端成功设置。
太好了!🥳 我们成功设置了Permify的授权服务。现在我们的API已经开始运行,授权模型也已经配置好,准备就绪!
在下一步,我们将创建用于权限控制的中间件。
# 我们来搭建访问控制的中间件。
这里我将展示一个使用Express中间件来实现路由上的基于角色的权限控制的例子。
你还将学习如何在中间件中实现 Permify 访问检查端点(用于验证用户的角色和权限是否合法),并在验证通过后再允许访问受保护资源。
// auth.js
// 导入 Permify 客户端
const permify = require('@permify/permify-node');
const client = new permify.grpc.newClient({
endpoint: "localhost:3478",
});
// 检查用户权限的中间件函数
const checkPermissions = (permissionType) => {
return async (req, res, next) => {
try {
// 确保 req.params.id 存在
if (!req.params.id) {
throw new Error('请求参数中缺少用户ID');
}
// 将 permissionType 转换为字符串
const permTypeString = String(permissionType);
// 准备用于 Permify 检查的数据
const checkRes = await client.permission.check({
tenantId: 't1',
metadata: {
schemaVersion: '',
snapToken: '',
depth: 20,
},
entity: {
type: 'organization',
id: "1",
},
permission: permTypeString, // 使用转换后的 permissionType
subject: {
type: 'user',
id: req.params.id,
},
});
if (checkRes.can === 1) {
// 用户已授权
req.authorized = '授权';
next();
} else {
// 用户未授权
req.authorized = '未授权';
next();
}
} catch (err) {
console.error('检查权限时出错:', err.message); // 记录实际错误信息
res.status(500).send(err.message); // 将实际错误信息发送给客户端以便调试
}
};
};
module.exports = checkPermissions;
下面的代码旨在实现一个名为[checkPermission](https://docs.permify.co/api-reference/permission/check-api)的中间件函数,用来验证用户的权限,根据提供的权限类型。
执行时,它会从请求参数中提取用户ID,如果需要将权限类型转为字符串,然后通过Permify的`"permission.check"`方法向服务器发送权限检查请求。授权成功的话,就在请求对象里加`"authorized"`,否则加`"not authorized"`。
错误将进一步被记录下来并返回给客户端以便他们进行调试。
接下来,我们将刚才创建的中间件集成到我们的Node.js和Express应用中,以实施基于角色的访问控制(RBAC)、确保只有授权用户才能访问特定路由,他们需要具备适当的角色和权限。
# 步骤 6:通过 RBAC 保护路由
现在,让我们使用我们刚刚创建的中间件来确保我们的路由安全。我们将使用 `checkPermissions` 中间件来保护我们应用程序中的路由。
// app.js
// 导入所需的模块
const express = require('express');
const permify = require("@permify/permify-node");
const authMiddleware = require('./auth'); // 导入 auth 中间件
// 创建 Express 应用程序
const app = express();
// 定义自定义中间件来填充 userInfo 对象
app.use((req, res, next) => {
// 模拟用户认证并填充 userInfo 对象
req.userInfo = {
id: req.params.id // 从请求参数中提取 id
// 如有需要,可以添加其他用户信息
};
next();
});
// 定义路由
// 定义 '/users/:id' 路由,强制执行权限检查
app.get('/users/viewFiles/:id', authMiddleware('view_files'), (req, res) => {
// 如果中间件允许请求通过,则处理路由逻辑
if (req.authorized === 'authorized') {
res.send('您有权访问此用户资源');
} else {
res.status(403).send('您没有权限访问此用户资源');
}
});
// 定义 '/admin/deleteVendorFiles/:id' 路由,强制执行权限检查
app.get('/admin/deleteVendorFiles/:id', authMiddleware('delete_vendor_file'), (req, res) => {
// 如果中间件允许请求通过,则处理路由逻辑
if (req.authorized === 'authorized') {
res.send('您有权访问此管理员资源');
} else {
res.status(403).send('您没有权限访问此管理员资源');
}
});
// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在端口 ${PORT} 上运行`);
});
此代码将在端口3000上启动您的Express应用程序,并使用名为`authMiddleware`的自定义中间件来保护特定路由,确保它们的安全性。
这个中间件从`auth.js`文件导入,用于权限验证。它与Permify集成,并保护以下路由。
由 `authMiddleware` 保护的路由:
* 路由 `/users/viewFiles/:id`:此路由确保只有有权查看文件的用户才能访问此路由。
* 路由 `/admin/viewFiles/:id`:此路由确保只有有权删除供应商文件的管理员才能访问此路由。
将 `authMiddleware` 应用于这些路由,这样访问将根据 Permify 授予的权限进行限制,实现了访问控制。
让我们来测试一下我们的实现成果!
# 测试 RBAC 实施
比如说,我们有一个用户,她的userID是`alice`,我们来测试一下,看alice能否访问这个只有管理员、经理或非代理代理人能访问的 `/users/viewFiles/` API端点,正如我们在前面提到的那样。

果然,UserID `alice` 没有访问此 API 端点的权限。让我们用 Permify Nodejs 客户端的 [data.write](https://docs.permify.co/api-reference/data/write-data) 方法给 `alice` 成员角色。
// write-relationship.js
const permify = require("@permify/permify-node");
const client = new permify.grpc.newClient({
endpoint: "localhost:3478",
})
client.data.write({
tenantId: "t1",
metadata: {
schemaVersion: ""
},
tuples: [{
entity: {
type: "organization",
id: "1"
},
relation: "成员",
subject: {
type: "user",
id: "alice"
}
}]
}).then((response) => {
// 处理一下响应
console.log(response)
})
我们来运行这段代码,然后使用 Postman 调用 `/users/viewFiles/` API端点。

现在,Alice 可以成功访问 `/users/viewFiles/` 这个 API 了。
# 所以
# 或者使用总之,如果更适合非正式场合。如果是为了社交媒体并且保留#号,也可以保留#号。
重要的是要认识到,授权不是一个一次性配置的过程;而是一个持续的过程。
因此,定期检查你的模型,在测试后进行必要的调整,并随着应用程序的演变进行相应的修改非常重要。
本指南将帮助你在你的 Node.js 应用中实现 RBAC 提供一个坚实的基础来实施 RBAC。
不过,不要犹豫深入研究并根据您的实际需求调整 RBAC 模型。
利用Permify的功能,你可以简化权限管理流程,从而营造一个稳定且安全的应用环境。
共同學(xué)習(xí),寫下你的評(píng)論
評(píng)論加載中...
作者其他優(yōu)質(zhì)文章