使用typegraphql-prisma如何实现鉴权,避免用户删除他人Post资源
实现方案
typegraphql-prisma 完全支持通过中间件、resolver 增强的方式给自动生成的 CRUD resolver 加鉴权逻辑,不需要手动重写整套 mutation 代码,以下是两种常用实现方式:
方案1:使用applyResolversEnhanceMap绑定权限守卫(官方推荐)
这是 typegraphql-prisma 专门提供的扩展能力,可以针对单个模型的单个操作绑定自定义校验逻辑,侵入性最低。
- 首先编写自定义的 Post 所有者校验守卫,里面可以获取 GraphQL 上下文中的当前登录用户信息、请求入参,完成所有者校验:
import { Guard, IGuardArgument } from "@typegraphql/prisma"; import { Context } from "./context"; // 你的上下文类型,包含当前登录用户、prisma实例等 export class PostOwnerGuard implements Guard<Context> { async canActivate({ context, args }: IGuardArgument<Context>) { // 先判断用户是否登录 const currentUserId = context.currentUser?.id; if (!currentUserId) return false; // 获取要操作的Post ID const targetPostId = args.where.id; // 查库获取目标Post的所有者ID const targetPost = await context.prisma.post.findUnique({ where: { id: targetPostId }, select: { authorId: true } }); if (!targetPost) return false; // 校验是否是所有者 return targetPost.authorId === currentUserId; } }
- 然后在初始化自动生成 resolver 的文件中,用
applyResolversEnhanceMap为 deletePost 操作绑定这个守卫即可:
import { applyResolversEnhanceMap, ResolverActionsConfig } from "@typegraphql/prisma"; import { PostOwnerGuard } from "./post-owner.guard"; const postResolverConfig: ResolverActionsConfig<"Post"> = { // 给删除、更新操作都加所有者校验 deletePost: [UseGuard(PostOwnerGuard)], updatePost: [UseGuard(PostOwnerGuard)] }; applyResolversEnhanceMap({ Post: postResolverConfig });
配置完成后,所有针对 Post 的删除、更新请求都会先走守卫的校验逻辑,非所有者的请求会直接被拦截,你也可以在校验不通过时自定义返回值,比如直接返回 null。
方案2:使用全局 GraphQL 中间件统一拦截
如果你需要给所有模型的修改、删除操作都加类似的所有者校验,可以用全局中间件统一处理,不需要逐个配置每个模型的守卫:
- 编写全局中间件:
import { MiddlewareFn } from "type-graphql"; import { Context } from "./context"; export const OwnerCheckMiddleware: MiddlewareFn<Context> = async ({ context, info, args }, next) => { const operationName = info.fieldName; // 只拦截删除、更新类操作 if (operationName.startsWith("delete") || operationName.startsWith("update")) { const currentUserId = context.currentUser?.id; if (!currentUserId) throw new Error("未授权访问"); // 提取操作的模型名 const modelName = operationName.replace(/delete|update/, "").toLowerCase(); // 查库获取目标资源的所有者ID,这里可以根据不同模型调整关联用户的字段名 const targetRecord = await context.prisma[modelName].findUnique({ where: args.where, select: { authorId: true, userId: true } }); const ownerId = targetRecord?.authorId || targetRecord?.userId; if (ownerId && ownerId !== currentUserId) { // 校验不通过可以抛错或者直接返回null throw new Error("无权限操作该资源"); } } return next(); };
- 在构建 Schema 的时候注册全局中间件即可:
import { buildSchema } from "type-graphql"; const schema = await buildSchema({ resolvers: [/* 你的所有resolver,包括自动生成的CRUD resolver */], globalMiddlewares: [OwnerCheckMiddleware] });
如果有特殊场景需要自定义逻辑,你也可以直接覆盖自动生成的对应 mutation resolver,自己编写完整的校验和业务逻辑,和普通自定义 resolver 的写法完全一致。
内容的提问来源于stack exchange,提问作者dsmurl
相关产品推荐
相关产品推荐

