后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载在基于 Prisma 与graphql-yoga构建的 GraphQL 服务中权限控制并不是数据库层面的功能而是由开发者在自己编写的 resolver 中实现的数据访问检查。本文以官方教程为基础完整演示如何从零搭建一个带认证的 GraphQL 服务器通过修改数据模型引入ADMIN角色并利用prisma-binding提供的exists函数逐个为feed、drafts、post、publish、deletePost五个查询/变更实现细粒度的访问控制。读完本文你将掌握应用层权限校验这一 Prisma 时代标准实践并能直接将其复用到自己的 GraphQL 服务中。背景Prisma 时代的权限控制思路在 Graphcool Framework 时代权限规则permission queries被声明式地配置在框架内部。而到了 Prisma 时代这一职责被明确移交到了应用层——也就是你的 GraphQL 服务器代码中。正如仓库文档 Authentication Authorization 所总结的User authentication and permission rules are now implemented in the application layer of your GraphQL server. Theexistsfunction from theprisma-bindingpackage serves a similar purpose as permissions queries inside the Graphcool Framework.这意味着每当你需要限制某个操作时就在对应的 resolver 里先做一次数据访问检查检查通过后才把操作转发给底层的 Prisma 数据库服务。而prisma-binding包在node-advanced样板工程中暴露为ctx.db正是这一转发的关键桥梁。引导 GraphQL 服务器安装 GraphQL CLI在开始之前需要先安装 GraphQL CLI。打开终端执行npm install -g graphql-cli说明本教程不需要单独安装 Prisma CLI因为node-advanced样板工程将prisma列为development dependency因此可以通过yarn前缀运行它的命令例如yarn prisma deploy或yarn prisma playground。如果你已在全局安装prismanpm install -g prisma则可以省略yarn前缀。创建项目在任意目录下运行graphql create permissions-example --boilerplate node-advanced当被询问要将 Prisma 服务部署到哪个cluster时选择一个公共演示集群prisma-eu1或prisma-us1。注意你也可以在本地部署 Prisma 服务但这要求机器上装有 Docker。本教程选用公共演示集群以保持流程简单直接。该命令会创建名为permissions-example的新目录其中包含基于graphql-yoga的 GraphQL 服务器源码以及对应 Prisma 数据库服务的配置文件。node-advanced样板工程自带开箱即用的认证功能signup / login并且预配置好了prisma-binding与若干示例 resolver——这正是我们接下来要逐步改造的基础。这个 GraphQL 服务器基于以下数据模型对应 Prisma 服务的database/datamodel.graphqltype Post { id: ID! unique createdAt: DateTime! updatedAt: DateTime! isPublished: Boolean! default(value: false) title: String! text: String! author: User! } type User { id: ID! unique email: String! unique password: String! name: String! posts: [Post!]! }为应用添加ADMIN角色在本教程的场景中User要么是拥有特殊访问权限的管理员ADMIN要么是普通客户CUSTOMER。为了区分这两类用户需要对数据模型做一处修改为User增加role字段并新增Role枚举。打开database/datamodel.graphql将User类型更新为如下形态同时补上Role枚举type User { id: ID! unique email: String! unique password: String! name: String! posts: [Post!]! role: Role! default(value: CUSTOMER) } enum Role { ADMIN CUSTOMER }注意role字段以及password字段并不会通过 GraphQL 服务器暴露给客户端因为应用 schemaapplication schema中的User类型并未包含它们。应用 schema 最终决定了哪些数据会暴露给你的客户端应用——数据模型字段与 API 暴露字段相互独立这正是 Prisma 架构中数据库层与应用层分离的体现。修改完成后需要部署数据库使变更生效。在permissions-example目录中运行yarn prisma deploy此时数据模型与 Prisma API 已更新User类型包含了role字段。定义权限需求src/schema.graphql中定义的应用 schema 暴露了以下查询与变更type Query { feed: [Post!]! drafts: [Post!]! post(id: ID!): Post! me: User } type Mutation { signup(email: String!, password: String!, name: String!): AuthPayload! login(email: String!, password: String!): AuthPayload! createDraft(title: String!, text: String!): Post! publish(id: ID!): Post! deletePost(id: ID!): Post! }本教程只关注与Post类型相关的 resolver它们的权限需求如下操作权限要求feed无权限要求。所有人包括未认证用户都可以访问已发布Post节点的feeddrafts每个用户只能访问自己的草稿即自己是该Post的authorpost只有Post的author或ADMIN用户可以使用post查询访问Post节点publish只有Post的author可以发布它deletePost只有Post节点的author或ADMIN用户可以删除它用graphql-yoga与 Prisma 实现权限规则实现权限规则的基本思路是在每个 resolver 中实现一次数据访问检查只有检查通过该操作查询、变更或订阅才会通过可用的prisma-binding被转发给 Prisma 服务。这里的检查工具是prisma-binding的exists函数。从源码层面看这个能力来自 Prisma 客户端基类的buildExists()实现见 cli/packages/prisma-client-lib/src/Client.ts客户端会遍历 schema 的 Query 类型为数据模型中的每个类型自动生成一个以类型名命名的 exists 函数调用时它内部执行对应类型的列表查询如query.posts({ where })并根据返回结果长度是否大于 0 返回布尔值private buildExists(): Exists { const queryType this._schema.getQueryType() // ... return types.reduce((acc, { type, pluralFieldName }) { const firstLetterLowercaseTypeName type[0].toLowerCase() type.slice(1) return { ...acc, [firstLetterLowercaseTypeName]: args { return thispluralFieldName.then(res { return res.length 0 }) }, } }, {}) }这意味着ctx.db.exists.Post({ ... })与ctx.db.exists.User({ ... })都是根据你的数据模型自动生成的无需手写底层查询。正如仓库中的 prisma-binding 文档所述exists是Prisma实例上的公共属性与query、mutation类似每个类型暴露一个函数接收where对象作为输入返回布尔值表示该条件是否满足。下面逐步为各 resolver 加上这些检查。feed由于所有人都能访问feed查询这里无需实现任何检查。drafts需求是每个用户只能访问自己的草稿即自己是Post的author。当前的draftsresolver 实现如下drafts(parent, args, ctx, info) { const id getUserId(ctx) const where { isPublished: false, author: { id } } return ctx.db.query.posts({ where }, info) },事实上这段代码已经满足了需求——它通过where过滤条件只检索出属于当前认证用户getUserId(ctx)从请求上下文解析出用户 id的草稿。因此这里无需任何改动。post需求是只有Post的author或ADMIN用户可以使用post查询访问Post节点。当前的实现非常直接post(parent, { id }, ctx, info) { return ctx.db.query.post({ where: { id } }, info) }但现在需要确保只有请求者是该Post的author或ADMIN用户时才返回该Post。为此我们使用prisma-binding的exists函数。将src/resolvers/Query.js中的实现更新为async post(parent, { id }, ctx, info) { const userId getUserId(ctx) const requestingUserIsAuthor await ctx.db.exists.Post({ id, author: { id: userId, }, }) const requestingUserIsAdmin await ctx.db.exists.User({ id: userId, role: ADMIN, }) if (requestingUserIsAdmin || requestingUserIsAuthor) { return ctx.db.query.post({ where: { id } }, info) } throw new Error( Invalid permissions, you must be an admin or the author of this post to retrieve it., ) }通过两次exists调用我们收集到两方面的信息发送请求的User是否确实是所请求Post的author发送请求的User是否是ADMIN。只要任一条件为真就直接返回该Post否则抛出一个权限不足的错误。publishpublish变更的需求是只有Post的author可以发布它。该 resolver 位于src/resolvers/Mutation/post.js当前实现如下async publish(parent, { id }, ctx, info) { const userId getUserId(ctx) const postExists await ctx.db.exists.Post({ id, author: { id: userId }, }) if (!postExists) { throw new Error(Post not found or youre not the author) } return ctx.db.mutation.updatePost( { where: { id }, data: { isPublished: true }, }, info, ) },当前的exists调用已经确保了请求用户是该待发布Post的author。所以这里同样不需要做任何修改需求已得到满足。deletePostdeletePost变更的需求是只有Post节点的author或ADMIN用户可以删除它。当前 resolver 位于src/resolvers/Mutation/post.js实现如下async deletePost(parent, { id }, ctx, info) { const userId getUserId(ctx) const postExists await ctx.db.exists.Post({ id, author: { id: userId }, }) if (!postExists) { throw new Error(Post not found or youre not the author) } return ctx.db.mutation.deletePost({ where: { id } }) },与publish类似exists调用已确保请求用户是待删除Post的author。但这里还需补上一种情形如果该用户是ADMINPost依然应该被删除。将src/resolvers/Mutation/post.js中的deletePost调整为async deletePost(parent, { id }, ctx, info) { const userId getUserId(ctx) const postExists await ctx.db.exists.Post({ id, author: { id: userId }, }) const requestingUserIsAdmin await ctx.db.exists.User({ id: userId, role: ADMIN, }) if (!postExists !requestingUserIsAdmin) { throw new Error(Post not found or you dont have access rights to delete it.) } return ctx.db.mutation.deletePost({ where: { id } }) },注意这里的判断逻辑只有不是作者且不是管理员时才拒绝请求二者满足其一即可删除。要点回顾观察post与deletePost的完整版实现可以看出管理员ADMIN权限在应用层的表达方式是一致的——通过ctx.db.exists.User({ id: userId, role: ADMIN })判定请求者角色。这套模式在仓库的 Authentication Authorization 迁移指南中同样被用于updatePost等场景是从 Graphcool 迁移到 Prisma 时的标准写法。测试权限可以在 GraphQL Playground 中测试这些权限规则整体流程如下在 Playground 中用signup变更创建一个新User并在 selection set 中指定token使其由服务器返回如果之前已创建过User也可以使用login变更将服务器响应中的token保存下来设置为 Playground 的Authorization请求头具体方法见下文此后所有请求都将代表该User发出。1. 创建新User首先启动服务器。在permissions-example目录下运行yarn start服务器现在运行在http://localhost:4000。在浏览器中打开该地址在app部分的defaultPlayground 中发送以下变更mutation { signup( email: sarahgraph.cool password: graphql name: Sarah ) { token } }2. 设置Authorization请求头复制返回的token将其设置为 Playground 左下角的Authorization请求头。请求头需要以 JSON 形式设置注意把__TOKEN__占位符替换为signup变更返回的认证token{ Authorization: __TOKEN__ }从现在起通过 Playground 发送的所有请求都代表刚创建的这个User。3. 验证权限规则掌握上述方法后你就可以操作可用的查询与变更验证权限规则是否生效。例如可以走一遍下面的流程以Sarah刚创建的User的身份用createDraft变更创建一个新草稿再用signup变更创建另一个User并为它获取一个token使用新用户的认证token尝试发布 Sarah 的草稿。此时应返回错误Post not found or youre not the author。如果上述流程得到预期结果说明publish的仅作者可发布规则已正确生效同理你也可以切换不同角色验证post与deletePost中作者或管理员可访问/删除的规则以及feed的完全公开访问。总结通过本教程你完成了以下完整链路使用graphql create --boilerplate node-advanced引导了一个自带认证的 GraphQL 服务器与对应的 Prisma 数据库服务通过修改数据模型并执行yarn prisma deploy为User引入了role: Role!字段与ADMIN/CUSTOMER枚举明确了五个Post相关操作的权限需求并利用prisma-binding自动生成的ctx.db.exists.*函数在 resolver 中逐个实现了数据访问检查——凡是已有检查满足需求的地方drafts、publish保持原样凡是需要补充管理员判断的地方post、deletePost追加了ctx.db.exists.User({ id, role: ADMIN })分支在 GraphQL Playground 中通过signup获取 token、设置Authorization请求头验证了跨用户越权访问会被拒绝。从源码层面看exists系列函数由 cli/packages/prisma-client-lib/src/Client.ts 中的buildExists()基于数据模型自动生成其内部通过列表查询的结果长度判断条件是否成立。这套应用层权限校验 委托转发的模式是 Prisma 架构下实现权限控制的标准实践数据模型决定数据库结构应用 schema 决定 API 暴露面resolver 中的exists检查决定谁能对哪些数据执行什么操作。赞分享后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载相关推荐基于 Prisma 与 graphql-yoga 实现 GraphQL 权限控制从数据模型到 Resolver 实战基于 Prisma 与 graphql yoga 实现 GraphQL 权限控制从数据模型到 Resolver 实战 本文是一篇面向 GraphQL 服务端开后端数据库GraphQLPrisma 与 graphql-yoga 实现 GraphQL API 权限控制从角色建模到 resolver 级访问检查Prisma 与 graphql yoga 实现 GraphQL API 权限控制从角色建模到 resolver 级访问检查 导读 本教程以 Prisma 官后端数据库GraphQL使用 Prisma 与 graphql-yoga 实现 GraphQL 权限控制从角色建模到 resolver 级访问校验使用 Prisma 与 graphql yoga 实现 GraphQL 权限控制从角色建模到 resolver 级访问校验 本文基于 Prisma 官方教程后端数据库GraphQL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
