跳至主要内容

如何在 Permit.io 中使用 Prisma ORM

15 分钟

介绍

Permit.io 是一个授权即服务平台,允许您根据真实世界的关联实现细粒度访问控制规则。

本指南解释了如何将 Permit.io 连接到新的 Express + Prisma 应用程序,定义基于关系的访问控制 (ReBAC) 策略,并自动过滤 Prisma 查询,以便用户只能看到他们被允许访问的数据。

您将构建一个小型项目任务 API 来演示访问继承的实际应用——无需手动添加 WHERE 子句。

您可以在此处找到本指南的完整示例。

先决条件

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) 来根据用户与数据的关系自动限制数据库查询。

让我们看看这是什么样子的

ReBAC authorization model

场景概述

您正在构建一个支持团队级访问控制的项目管理 API。每个项目都属于一个团队(如市场部或工程部),用户应该只能访问他们被分配到的项目及其相关的任务。

这是一个完美的 ReBAC 用例,因为

  • 访问取决于用户与数据之间的关系(例如,团队成员身份)
  • 您希望任务访问从项目访问继承
  • 您希望避免在每个控制器中手动检查权限

资源

这些是您将保护的主要数据实体

  • Project:表示可能包含业务关键数据(时间线、预算、客户可交付成果)的特定团队工作区。
  • Task:表示属于项目的工作项

关系

  • 项目包含任务(Project → Task
  • 用户是项目的成员(User → Project

实例级角色

实例级角色描述了用户可以使用特定资源做什么

角色描述
project#Member用户可以访问特定项目
task#Member用户可以访问该项目中的任务

角色派生

ReBAC 允许您根据关系自动派生角色。在这种情况下

  • 如果用户是 project#Member,他们将自动成为该项目内所有任务的 task#Member
  • 新任务继承项目访问权限——无需手动更新权限。

访问策略

一旦定义了关系和角色,访问策略将决定用户可以做什么

角色操作资源
project#Member读取项目
task#Member读取任务

此模型确保

  • 用户只能访问他们被分配到的项目和任务
  • 没有跨团队可见性
  • 访问权限自动与业务结构保持同步

Access control planning

3. 定义您的数据模型

为了支持权限感知数据过滤,您需要构建数据库,使关系清晰明确。在这种情况下,每个 Task 都属于一个 Project,用户通过成为父项目的成员来获得任务的访问权限。

3.1 更新您的 Prisma 模式

打开 prisma/schema.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 客户端
  • ProjectTask 创建表,并建立一对多关系

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
};
API 密钥

您可以在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

这将创建

  • 资源projecttask
  • 关系projecttask 的父级
  • 角色:
    • project#Member:用户可以访问特定项目
    • task#Member:从项目成员身份派生
  • 访问策略:具有相应角色的用户可以读取每个资源

6.4 在 Permit UI 中查看策略

转到Permit.io 仪表板并导航到您的环境以探索

  • 您的资源图
  • 角色派生
  • 关系映射
  • 读取访问的策略规则
信息

这些规则由 @permitio/permit-prisma 扩展用于自动确定为每个用户返回哪些记录。

有了您的策略,您现在就可以在 Express 中间件中连接用户上下文和过滤逻辑了。

7. 添加中间件来设置用户上下文

要按用户过滤 Prisma 查询,您需要

  1. 识别当前用户(通过电子邮件头模拟)
  2. 将过滤后的 Prisma 客户端实例附加到请求
  3. 在 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

即使您手动提供了 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 关系。
  • 引入具有不同权限的实例级角色,如 EditorOwner
  • 使用 Permit.io 的角色策略支持额外的操作,如 createupdatedelete

添加身份验证

将您的 API 与身份验证提供商(例如,Clerk、Auth0)集成,并用安全的身份机制(如 JWT 令牌)替换 x-user-email 标头。

使用 Permit Elements

Permit.io 提供用于以下目的的 UI 组件

  • 可视化管理用户访问
  • 审查访问日志
  • 批准访问请求 (MCP)

探索Permit Elements,使您的最终用户或管理员更容易进行访问管理。

更多资源


与 Prisma 保持联系

通过以下方式与我们保持联系,继续你的 Prisma 之旅: 我们的活跃社区。保持信息灵通,参与其中,并与其他开发人员协作。

我们真诚地感谢你的参与,并期待你成为我们社区的一部分!

© . This site is unofficial and not affiliated with Prisma Data, Inc.