如何在 Permit.io 中使用 Prisma ORM
介绍
Permit.io 是一个授权即服务平台,允许您根据真实世界的关联实现细粒度访问控制规则。
本指南解释了如何将 Permit.io 连接到新的 Express + Prisma 应用程序,定义基于关系的访问控制 (ReBAC) 策略,并自动过滤 Prisma 查询,以便用户只能看到他们被允许访问的数据。
您将构建一个小型项目任务 API 来演示访问继承的实际应用——无需手动添加 WHERE 子句。
您可以在此处找到本指南的完整示例。
先决条件
- Node.js v16+
- PostgreSQL(本地或托管)
- Prisma CLI(
npx prisma) - TypeScript
- Permit CLI(
npm install -g @permitio/cli)
1. 设置项目
首先,您将使用 TypeScript 从头开始创建一个新的 Express + Prisma 项目。您还将安装支持 Permit.io 进行 ReBAC 过滤所需的工具。
1.1 创建项目文件夹
mkdir prisma-rebac-filtering
cd prisma-rebac-filtering
npm init -y
1.2 安装所需依赖项
安装应用程序和开发依赖项
npm install express cors dotenv @prisma/client @prisma/adapter-pg pg
npm install -D prisma typescript tsx @types/pg
如果你使用的是其他数据库提供程序(MySQL、SQL Server、SQLite),请安装相应的驱动程序适配器包,而不是 @prisma/adapter-pg。有关更多信息,请参阅 数据库驱动程序。
然后,初始化您的 Prisma 设置
npx prisma init
这将创建
- 一个带有默认
schema.prisma文件的prisma/目录 - 一个用于配置 Prisma 的
prisma.config.ts文件 - 根目录下的
.env文件。
1.3 设置您的 TypeScript 配置
创建一个 tsconfig.json 文件
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"moduleResolution": "node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist"
},
"include": ["src", "scripts"]
}
1.4 创建您的文件夹结构
设置您的项目文件夹
mkdir -p src/controllers src/middleware src/config scripts
您现在已准备好定义您的 Prisma 数据模型。
2. 授权模型
在继续设置之前,定义应用程序中访问控制的工作方式非常重要。
本指南使用基于关系的访问控制 (ReBAC) 来根据用户与数据的关系自动限制数据库查询。
让我们看看这是什么样子的

场景概述
您正在构建一个支持团队级访问控制的项目管理 API。每个项目都属于一个团队(如市场部或工程部),用户应该只能访问他们被分配到的项目及其相关的任务。
这是一个完美的 ReBAC 用例,因为
- 访问取决于用户与数据之间的关系(例如,团队成员身份)
- 您希望任务访问从项目访问继承
- 您希望避免在每个控制器中手动检查权限
资源
这些是您将保护的主要数据实体
Project:表示可能包含业务关键数据(时间线、预算、客户可交付成果)的特定团队工作区。Task:表示属于项目的工作项
关系
- 项目包含任务(
Project → Task) - 用户是项目的成员(
User → Project)
实例级角色
实例级角色描述了用户可以使用特定资源做什么
| 角色 | 描述 |
|---|---|
project#Member | 用户可以访问特定项目 |
task#Member | 用户可以访问该项目中的任务 |
角色派生
ReBAC 允许您根据关系自动派生角色。在这种情况下
- 如果用户是
project#Member,他们将自动成为该项目内所有任务的task#Member。 - 新任务继承项目访问权限——无需手动更新权限。
访问策略
一旦定义了关系和角色,访问策略将决定用户可以做什么
| 角色 | 操作 | 资源 |
|---|---|---|
project#Member | 读取 | 项目 |
task#Member | 读取 | 任务 |
此模型确保
- 用户只能访问他们被分配到的项目和任务
- 没有跨团队可见性
- 访问权限自动与业务结构保持同步

3. 定义您的数据模型
为了支持权限感知数据过滤,您需要构建数据库,使关系清晰明确。在这种情况下,每个 Task 都属于一个 Project,用户通过成为父项目的成员来获得任务的访问权限。
3.1 更新您的 Prisma 模式
打开 prisma/schema.prisma 并替换为以下内容
generator client {
provider = "prisma-client"
}
datasource db {
provider = "postgresql"
}
model Project {
id String @id @default(uuid())
name String
tasks Task[] // One-to-many relationship for permission inheritance
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Task {
id String @id @default(uuid())
name String
description String?
projectId String
project Project @relation(fields: [projectId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
3.2 将 dotenv 添加到 prisma.config.ts
要访问 .env 文件中的变量,它们可以由您的运行时加载,或者通过使用 dotenv 加载。在 prisma.config.ts 顶部包含 dotenv 的导入
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config';
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations',
},
datasource: {
url: env('DATABASE_URL'),
},
});
3.3 运行您的第一次迁移
创建数据库模式
npx prisma migrate dev --name init
npx prisma generate
这将
- 将您的模式应用于连接的 PostgreSQL 数据库
- 生成您的 Prisma 客户端
- 为
Project和Task创建表,并建立一对多关系
3.3 确认结构
您可以打开 Prisma Studio 检查您的数据库
npx prisma studio
此结构允许 @permitio/permit-prisma 扩展在查询时按用户关系过滤记录。接下来,您将播种测试数据以模拟项目和任务的不同团队所有权。
4. 使用项目边界播种测试数据
为了测试您的数据过滤逻辑,您将创建两个项目,每个项目都有自己的任务集。这种分离模拟了团队所有权,并将允许您验证用户只看到他们分配到的项目的数据。
4.1 创建种子脚本
在 scripts/seed.ts 创建一个新文件并添加以下内容
import { PrismaClient } from '../src/generated/prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!,
});
const prisma = new PrismaClient({
adapter,
});
async function main() {
console.log('🌱 Seeding test data...');
// Clear existing records
await prisma.task.deleteMany();
await prisma.project.deleteMany();
// Create Project Alpha for the Marketing team
const projectAlpha = await prisma.project.create({
data: {
id: 'project_alpha',
name: 'Marketing Campaign Q2',
},
});
// Create Project Beta for the Engineering team
const projectBeta = await prisma.project.create({
data: {
id: 'project_beta',
name: 'API Development Sprint',
},
});
// Add tasks to Project Alpha
await prisma.task.createMany({
data: [
{
id: 'task-alpha-1',
name: 'Strategy Planning',
description: 'Define campaign goals and KPIs',
projectId: projectAlpha.id,
},
{
id: 'task-alpha-2',
name: 'Budget Review',
description: 'Review marketing budget with finance',
projectId: projectAlpha.id,
},
],
});
// Add tasks to Project Beta
await prisma.task.createMany({
data: [
{
id: 'task-beta-1',
name: 'Implement Auth API',
description: 'Create endpoints for user login/signup',
projectId: projectBeta.id,
},
{
id: 'task-beta-2',
name: 'Schema Migration',
description: 'Update tables for new user roles',
projectId: projectBeta.id,
},
],
});
console.log('✅ Seeded 2 projects and 4 tasks with distinct ownership');
}
main()
.catch((e) => {
console.error('❌ Error seeding data:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
4.2 运行种子脚本
npx tsx scripts/seed.ts
如果成功,您将看到
✅ 播种了 2 个项目和 4 个任务,具有不同的所有权
此时,如果您运行 prisma.task.findMany() 之类的查询,它将返回所有任务。在接下来的步骤中,您将连接 Permit.io 以根据用户的访问权限自动过滤这些结果。
5. 安装和配置 ReBAC 过滤
在本节中,您将安装 @permitio/permit-prisma 扩展并将其配置为根据您的访问控制策略自动过滤 Prisma 查询。
5.1 安装 Permit 扩展
安装 permit-prisma 包
npm install @permitio/permit-prisma
5.2 配置 Permit 客户端
在 src/config/permit-config.ts 创建一个新文件
import dotenv from 'dotenv';
dotenv.config();
export const clientExtensionConfig = {
permitConfig: {
token: process.env.PERMIT_API_KEY!, // Your Permit.io API key
pdp: process.env.PERMIT_PDP_URL || 'https://:7766', // Local or cloud PDP
debug: true,
},
enableAutomaticChecks: true,
enableDataFiltering: true, // Enables automatic query filtering
enableResourceSync: true // (Optional) Keeps Permit in sync with resource changes
};
您可以在Permit.io 仪表板中找到您的 API 密钥和 PDP URL。
5.3 此配置的作用
当您稍后使用此配置扩展 Prisma 客户端时
- 所有 Prisma 查询将自动检查访问规则
findMany()和类似方法将只返回用户被允许访问的数据- 您不再需要手动添加
WHERE子句来强制执行权限
您现在已准备好使用 Permit CLI 定义您的 ReBAC 策略。
6. 在 Permit.io 中定义您的访问控制策略
接下来,您将使用 Permit CLI 应用一个与您的项目任务结构匹配的现成 ReBAC 模板。
6.1 安装 Permit CLI
npm install -g @permitio/cli
6.2 登录您的 Permit 账户
使用 CLI 进行身份验证
permit login
这将打开一个浏览器窗口,您可以在其中登录您的 Permit.io 账户并将您的 CLI 会话链接到一个环境。
6.3 应用 ReBAC 策略模板
Permit 提供了一个用于分层数据过滤的预构建策略结构。
使用以下方式应用它
permit env template apply --template orm-data-filtering
这将创建
- 资源:
project、task - 关系:
project是task的父级 - 角色:
project#Member:用户可以访问特定项目task#Member:从项目成员身份派生
- 访问策略:具有相应角色的用户可以
读取每个资源
6.4 在 Permit UI 中查看策略
转到Permit.io 仪表板并导航到您的环境以探索
- 您的资源图
- 角色派生
- 关系映射
读取访问的策略规则
这些规则由 @permitio/permit-prisma 扩展用于自动确定为每个用户返回哪些记录。
有了您的策略,您现在就可以在 Express 中间件中连接用户上下文和过滤逻辑了。
7. 添加中间件来设置用户上下文
要按用户过滤 Prisma 查询,您需要
- 识别当前用户(通过电子邮件头模拟)
- 将过滤后的 Prisma 客户端实例附加到请求
- 在 Permit 上下文中设置用户(
prisma.$permit.setUser())
7.1 创建中间件文件
创建一个新文件:src/middleware/auth.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { PrismaClient } from '../generated/prisma/client.js';
import { PrismaPg } from '@prisma/adapter-pg';
import createPermitClientExtension from '@permitio/permit-prisma';
import { clientExtensionConfig } from '../config/permit-config';
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!,
});
// Extend PrismaClient with Permit
const prisma = new PrismaClient({
adapter,
}).$extends(
createPermitClientExtension(clientExtensionConfig)
);
// Extend Request type with Prisma and user context
export interface AuthRequest extends Request {
user?: { email: string };
prisma?: typeof prisma;
}
export const authenticate = (
req: AuthRequest,
res: Response,
next: NextFunction
): void => {
const userEmail = req.headers['x-user-email'] as string;
if (!userEmail) {
res.status(401).json({ error: 'Missing user email' });
return;
}
// Register the user in Permit context
prisma.$permit.setUser(userEmail);
// Add user + Prisma client to request
req.user = { email: userEmail };
req.prisma = prisma;
next();
};
在生产应用程序中,您将用适当的身份验证逻辑(例如 JWT 或会话验证)替换 x-user-email 头部。
7.2 此中间件的作用
- 从请求头中读取用户电子邮件
- 在 Permit 上下文中设置用户身份(用于查询过滤)
- 将过滤后的 Prisma 客户端添加到请求对象 (
req.prisma) - 使用户和数据库客户端可用于所有下游路由处理程序
您现在已准备好构建您的 API 端点,而无需编写任何访问控制逻辑。
8. 构建您的 API 端点
您现在将创建两个端点
GET /api/projects:返回用户有权访问的所有项目GET /api/tasks:返回用户有权访问的所有任务(从项目成员身份继承)
由于 Permit-Prisma 集成,您无需添加任何手动过滤逻辑——它将自动处理。
8.1 获取用户可见项目
创建一个控制器文件:src/controllers/project.controller.ts
import { Response } from 'express';
import { AuthRequest } from '../middleware/auth.middleware';
export const getProjects = async (req: AuthRequest, res: Response) => {
try {
const prisma = req.prisma!;
const projects = await prisma.project.findMany(); // Auto-filtered
res.json({
user: req.user?.email,
count: projects.length,
projects,
});
} catch (error: any) {
console.error('Error fetching projects:', error);
res.status(500).json({ error: error.message });
}
};
即使这是一个原始的 findMany() 查询,也只会为当前用户返回授权记录。
8.2 获取用户可见任务
创建另一个控制器:src/controllers/task.controller.ts
import { Response } from 'express';
import { AuthRequest } from '../middleware/auth.middleware';
export const getTasks = async (req: AuthRequest, res: Response) => {
try {
const prisma = req.prisma!;
const projectId = req.query.projectId as string;
const where = projectId ? { projectId } : undefined;
const tasks = await prisma.task.findMany({ where }); // Still filtered
res.json({
user: req.user?.email,
count: tasks.length,
tasks,
});
} catch (error: any) {
console.error('Error fetching tasks:', error);
res.status(500).json({ error: error.message });
}
};
即使您手动提供了 projectId,查询结果仍然会根据权限进行过滤。
8.3 这演示了什么
- 您可以编写正常的 Prisma 查询
- 用户只会获得他们被允许查看的记录
- 您不需要在每个处理程序中自定义角色检查逻辑
- 任务访问权限是自动从项目成员身份派生的
您现在已准备好将所有内容连接起来并启动应用程序。
9.1 创建您的 Express 应用程序
创建 src/app.ts
import express from 'express';
import cors from 'cors';
import { authenticate } from './middleware/auth.middleware';
import { getProjects } from './controllers/project.controller';
import { getTasks } from './controllers/task.controller';
const app = express();
const PORT = process.env.PORT || 3000;
app.use(cors());
app.use(express.json());
// Auth middleware applies ReBAC filtering per request
app.get('/api/projects', authenticate, getProjects);
app.get('/api/tasks', authenticate, getTasks);
app.listen(PORT, () => {
console.log(`🚀 Server running at https://:${PORT}`);
console.log(`🔐 ReBAC filtering is now active`);
});
9.2 运行服务器
使用以下命令启动开发服务器
npx tsx src/app.ts
如果一切设置正确,控制台将显示
🚀 Server running at https://:3000
🔐 ReBAC filtering is now active
9.3 测试您的 API
您可以通过设置 x-user-email 标头来模拟不同用户的请求。这模拟了具有特定项目访问权限的登录用户。
示例:John(市场团队成员)
curl -H "x-user-email: john@company.com" https://:3000/api/projects
这应该只返回 Project Alpha(及其任务)。
示例:Mary(工程团队成员)
curl -H "x-user-email: mary@company.com" https://:3000/api/tasks
这应该只返回 Project Beta 的任务。
如果您尚未在 Permit.io UI 中将用户分配到项目成员身份,请访问策略编辑器并将用户分配到角色 (project#Member)。
一旦您确认了这些结果,您的 Prisma API 现在正在强制执行安全、基于关系的访问控制,所有这些都无需在您的代码中任何地方添加手动过滤逻辑。
您现在已经构建了一个安全的 API,它
- 根据用户关系过滤查询结果
- 使用 ReBAC 避免角色爆炸和脆弱的权限逻辑
- 保持 Prisma 查询的整洁、安全和可扩展
10. 后续步骤
现在您已经成功地使用 Prisma 和 ReBAC 实现了数据过滤,您可以扩展此基础以支持更复杂的授权用例和开发者工具。
扩展您的模型
- 添加
User模型并在用户和项目之间创建多对多Membership关系。 - 引入具有不同权限的实例级角色,如
Editor或Owner。 - 使用 Permit.io 的角色策略支持额外的操作,如
create、update和delete。
添加身份验证
将您的 API 与身份验证提供商(例如,Clerk、Auth0)集成,并用安全的身份机制(如 JWT 令牌)替换 x-user-email 标头。
使用 Permit Elements
Permit.io 提供用于以下目的的 UI 组件
- 可视化管理用户访问
- 审查访问日志
- 批准访问请求 (MCP)
探索Permit Elements,使您的最终用户或管理员更容易进行访问管理。
更多资源
与 Prisma 保持联系
通过以下方式与我们保持联系,继续你的 Prisma 之旅: 我们的活跃社区。保持信息灵通,参与其中,并与其他开发人员协作。
- 在 X 上关注我们 获取公告、直播活动和实用技巧。
- 加入我们的 Discord 提问、与社区交流,并通过对话获得积极支持。
- 在 YouTube 上订阅 获取教程、演示和直播。
- 在 GitHub 上参与 加星收藏存储库、报告问题或为问题做出贡献。