Prisma 认证与授权迁移指南:从 Graphcool Framework 迁移 Authentication 到应用层

发布时间:2026/9/24 11:36:09
Prisma 认证与授权迁移指南:从 Graphcool Framework 迁移 Authentication 到应用层 后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载本指南讲解如何将原有 Graphcool Framework 服务中的认证Authentication与授权Authorization功能迁移到 Prisma 架构Prisma 不再把用户认证与权限系统绑定而是提供基于 JWTService Token的简单令牌机制用户注册/登录与数据访问权限如只有作者才能更新自己的 Post全部下沉到 GraphQL 服务器的应用层实现。读完本文你将掌握签名与校验 Prisma 服务令牌、用prisma-binding的exists函数替代 Graphcool 权限查询permission queries以及用graphql-yogabcryptjsjsonwebtoken完整落地 signup / login / 权限校验的实战方案。背景Graphcool Framework 的认证与授权方式在 Graphcool Framework 中认证功能通过 resolver functions解析器函数实现对 API 的 GraphQL schema 做扩展追加提供注册signup和登录login能力的专用 mutation。整个过程通常包含三个步骤在 GraphQL schema 的Mutation类型上以 schema extension 的形式定义signup/loginresolver 函数直接在 Graphcool Framework 中以 JavaScript 提供这些 resolver 函数的实现或使用 webhook 调用你自行托管的函数在 service 定义文件中把 mutation 定义与实现连接起来。而在数据访问的安全控制方面Graphcool Framework 使用permission queries权限查询概念每个 API 操作都可以关联一条或多条权限规则在执行操作前先进行校验。这种把认证与权限都耦合进框架的方式带来的架构问题是认证逻辑、业务逻辑与数据层界限模糊。Prisma 对此做了彻底调整。Prisma 的认证概念为什么功能被移入应用层Prisma 的 authentication 概念与 Graphcool Framework 截然不同Prisma 不提供用户级认证。它只提供一个简单的、基于令牌可以理解为API Key的系统用于访问 Prisma API——即 service secret 与 service token 机制用户认证与权限规则移入应用层即你的 GraphQL 服务器graphql-yoga等负责实现prisma-binding包提供的exists函数在应用层扮演与 Graphcool Framework 中权限查询类似的角色JWT 令牌改由你自己生成而不再由graphcool-lib代为生成。这一设计对应迁移指南 Overview 中描述的两层架构Prisma 提供数据库层自动生成、面向 CRUD 的 GraphQL API你的 GraphQL 服务器构成应用层面向客户端、承载业务逻辑、认证与权限。prisma-binding负责把应用层的请求转发给 Prisma API。理解 Prisma 的服务密钥与令牌机制在动手迁移之前先理解 Prisma 自身的认证基础。Prisma 服务的 GraphQL API 默认由 service secret 保护它定义在prisma.yml中service: my-service stage: ${env:PRISMA_STAGE} cluster: ${env:PRISMA_CLUSTER} datamodel: database/datamodel.graphql secret: ${env:PRISMA_SECRET}secret的约束必须是 UTF-8 编码、不能包含空格、最长 256 字符也可以在单个字符串中以逗号分隔多个 secret空格会被忽略从而实现无缝密钥轮换secret: myFirstSecret, SECRET_NUMBER_2,3rd-secret如果设置disableAuth: true则任何人都拥有数据库的完整读写权限默认是false即默认启用认证。secret用于签发 JWT 服务令牌令牌放在 HTTP 请求的Authorization头中Authorization: Bearer __TOKEN__JWT 载荷payload包含两类必须的 claims{ exp: 1300819380, service: my-serviceprod }exp令牌过期时间service服务的名称与 stage。Prisma 校验请求时会检查令牌必须用服务配置的 secret 签名、exp必须指向未来、serviceclaim 必须与当前请求的服务及 stage 匹配。prisma.yml的完整字段定义可参考 YAML-Structure。在 Node 端可以基于jsonwebtoken库自行签发这样的服务令牌这里的PRISMA_SECRET/PRISMA_STAGE来自环境变量var jwt require(jsonwebtoken) jwt.sign( { data: { service: my-service process.env.PRISMA_STAGE, }, }, process.env.PRISMA_SECRET, { expiresIn: 1h, } )也可以直接用 CLI 命令prisma token获取当前 Prisma 服务的新签名 JWT支持--copy复制到剪贴板与--env-file指定环境变量文件详见 prisma-tokenprisma token prisma token --copy在源码层面prisma-client-lib的 Client 构造函数会在传入secret时自动用它签发空载荷令牌sign({}, secret)并把它作为Authorization: Bearer token头附加到对 Prisma API 的所有请求包括订阅的connectionParams。也就是说应用层服务器与 Prisma 之间的服务级认证是自动完成的你在应用层要操心的是你的用户与你的 GraphQL 服务器之间的用户认证。迁移前提以下迁移步骤假设你使用graphql-yoga作为 GraphQL 服务器你的 schema 定义使用 SDLSchema Definition Language编写。想直接看现成实现可参考本仓库examples/目录下的示例仓库中examples目录目前只包含说明文件完整的 auth / permissions 示例在 Prisma 官方示例仓库中。另有配套教程 Permissions 详述权限规则的实现。迁移 Step 1迁移 Schema 定义假设你的 Graphcool Framework 服务中曾定义过如下 schema extensiontype Mutation { signupUser(email: String!, password: String!): SignupUserPayload authenticateUser(email: String!, password: String!): AuthenticateUserPayload } type SignupUserPayload{ userId: ID! token: String! } type AuthenticateUserPayload { token: String! }其中signupUser与authenticateUsermutation 用于注册和登录。返回的token是由graphcool-lib生成的 JSON Web Token客户端把它放进AuthorizationHTTP 头即可对 API 发起认证请求。迁移时把这些定义挪进你的graphql-yoga服务器的 schema 定义即可。这里还有个额外的好处Graphcool 时期由于 resolver 函数无法返回模型类型model types而被迫做的 workaround 也可以一并去掉。例如新定义可以简化为type Mutation { signup(email: String!, password: String!): AuthPayload login(email: String!, password: String!): AuthPayload } type AuthPayload { token: String! user: User! }AuthPayload直接返回User对象schema 更干净也更能表达业务语义。迁移 Step 2迁移 Resolver 函数接下来为上面定义的signup和loginmutation 实现 resolver。核心变化是JWT 由你自己用jsonwebtoken生成密码哈希用bcryptjssignupresolver 中还负责创建新的User节点。auth.jsconst bcrypt require(bcryptjs) const jwt require(jsonwebtoken) const auth { async signup(parent, args, ctx, info) { const password await bcrypt.hash(args.password, 10) const user await ctx.db.mutation.createUser({ data: { ...args, password }, }) return { token: jwt.sign({ userId: user.id }, process.env.JWT_SECRET), user, } }, async login(parent, { email, password }, ctx, info) { const user await ctx.db.query.user({ where: { email } }) if (!user) { throw new Error(No such user found for email: ${email}) } const valid await bcrypt.compare(password, user.password) if (!valid) { throw new Error(Invalid password) } return { token: jwt.sign({ userId: user.id }, process.env.JWT_SECRET), user, } }, } module.exports { auth }几点说明signup中ctx.db.mutation.createUser是经由prisma-binding生成的 Prisma mutationdata: { ...args, password }中args包含email与password其中密码已替换为 bcrypt 哈希后的值——绝不能把明文密码写进数据库login用ctx.db.query.user({ where: { email } })按唯一字段email查询用户然后bcrypt.compare校验密码两个 resolver 都用jwt.sign({ userId: user.id }, process.env.JWT_SECRET)生成用户令牌这里的JWT_SECRET是应用层的密钥与 Prisma 的 service secret 不是一回事——它只用于签名你是谁的用户令牌。AuthPayload.js解析user字段避免把整棵User对象直接暴露const AuthPayload { user: async ({ user: { id } }, args, ctx, info) { return ctx.db.query.user({ where: { id } }, info) }, } module.exports { AuthPayload }把token与user解耦后可以确保只查询客户端真正请求的User字段info会被透传给 Prisma。如何识别当前请求用户在上面login之后所有受保护的 resolver 都要先从请求上下文恢复userId。常见做法是封装一个getUserId(ctx)工具从ctx.request的Authorization头中取出Bearer token用jsonwebtoken.verify验签得到userId验签失败则抛出未认证错误。这也是下方权限校验代码中getUserId(ctx)的语义基础——它在用户未认证时直接抛错。迁移 Step 3迁移权限规则如前所述prisma-binding的exists函数是迁移权限查询的首选工具。它生成的ctx.db.exists.Type(filter)会在数据库中检查是否存在满足条件的节点返回布尔值非常适合在 resolver 内做前置条件判断。在源码层面prisma-client-lib的 Client 类对外暴露$exists即ctx.db.exists的底层实现并在构造时通过this.buildExists()构建按模型类型生成exists.Post(...)/exists.User(...)这样的方法。场景只允许作者更新自己的 Post假设你的 Prisma 服务数据模型如下type User model { id: ID! unique name: String! posts: [Post!]! } type Post model { id: ID! unique title: String! author: User! }在 Graphcool Framework 中要表达只有Post的作者能更新它你需要给updatePostmutation 关联如下 permission queryquery ($user_id: ID!, $post_id: ID!) { SomePostExists(filter: { id: $post_id author: { id: $user_id } }) }而在 Prisma 中你需要在应用层的updatePostresolver 里完成同样的检查async function updatePost(parent, { id, title, text }, ctx, info) { // getUserId throws an error if the requesting user is not authenticated const userId getUserId(ctx) // this expresses the same condition as the permission query above const requestingUserIsAuthor await ctx.db.exists.Post({ id, author: { id: userId, }, }) // only if the condition is true, the post is actually updated if (requestingUserIsAuthor) { return await ctx.db.mutation.updatePost({ where: { id }, data: { title, text }, }, info) } throw new Error( Invalid permissions, you must be an admin or the author of a post to update it, ) }注意对应关系Graphcool FrameworkPrisma 应用层permission query如SomePostExistsctx.db.exists.Post({ ... })框架内自动执行权限检查resolver 内显式检查通过后才执行ctx.db.mutation.*$user_id由框架注入getUserId(ctx)从请求令牌解析检查失败由框架返回错误检查失败抛Error由 GraphQL 服务器统一返回扩展组合多个权限条件作者或管理员exists不仅支持单条条件还支持组合判断。比如作者本人或ADMIN用户才能读取某篇 Postasync 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., ) }这里的关键是给User增加role字段配合Role枚举ADMIN/CUSTOMER默认CUSTOMER且role与password一样不要暴露在应用层 schema中从而对客户端隐藏。更完整的可运行示例drafts、publish、deletePost等 resolver 的逐步改造参见 Permissions 教程。在 GraphQL Playground 中验证认证与权限完成迁移后可以用 GraphQL Playground 验证流程启动服务器yarn start打开http://localhost:4000发送signupmutation 创建用户并返回tokenmutation { signup( email: sarahgraph.cool password: graphql name: Sarah ) { token } }复制返回的token在 Playground 左下角的 HTTP Headers 中以 JSON 形式设置为Authorization头把__TOKEN__替换为真实令牌{ Authorization: __TOKEN__ }之后所有请求都代表该用户发出可以验证用另一个用户登录后尝试发布 Sarah 的 draft会得到Post not found or youre not the author之类的错误。小结从框架到 Prisma 的认证/授权心智模型迁移完成后你的架构变成了清晰的双 GraphQL 层Prisma数据库层只认 service tokenJWT由 service secret 签发提供纯粹的 CRUD 数据访问能力不做任何用户级权限判断你的 GraphQL 服务器应用层负责用户注册/登录、生成用户 JWT、解析当前请求用户getUserId、用ctx.db.exists实现细粒度权限校验再把合法请求转发给 Prisma。这套模式的收益是认证与授权逻辑完全由你掌控schema 更简洁AuthPayload直接返回user权限规则用普通 JavaScript 表达、可组合可测试同时 Prisma 侧的 API 可以保持通用、复用和可预测。赞分享后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载相关推荐Prisma 迁移指南从 Graphcool Framework 到 Prisma 的认证与授权Authentication AuthorizationPrisma 迁移指南从 Graphcool Framework 到 Prisma 的认证与授权Authentication Authorization后端数据库GraphQL从 Graphcool Framework 到 Prisma认证与授权Authentication Authorization迁移完整指南从 Graphcool Framework 到 Prisma认证与授权Authentication Authorization迁移完整指南 本篇指南以后端数据库GraphQLPrisma 认证与授权迁移指南从 Graphcool Framework 到应用层 JWT 实现Prisma 认证与授权迁移指南从 Graphcool Framework 到应用层 JWT 实现 本文基于 Prisma 官方迁移指南对应仓库文档 doc后端数据库GraphQL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考