ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TypeGraphQL 授权指南:使用 @Authorized 装饰器与 AuthChecker 构建声明式权限控制

TypeGraphQL 授权指南:使用 @Authorized 装饰器与 AuthChecker 构建声明式权限控制 后端GraphQLAPI设计【免费下载链接】type-graphqlCreate GraphQL schema and resolvers with TypeScript, using classes and decorators!项目地址https://gitcode.com/gh_mirrors/ty/type-graphql点击查看免费下载导读本文基于 TypeGraphQL 官方文档仓库website/versioned_docs/version-2.0.0-rc.3/authorization.md系统讲解 GraphQL API 中的授权Authorization机制。TypeGraphQL 把授权提升为一等公民特性通过Authorized装饰器在字段、查询、变更和 Resolver 类上声明权限元数据再配合自定义的AuthChecker在运行时统一校验从而摆脱传统 Node.js 框架里在每个 resolver 中手动调用鉴权函数、手工传递 context的繁琐写法。读完本文你将掌握Authorized的三种角色声明方式与类级别继承规则、AuthChecker函数式与类式两种实现形态、authMode的error/null两种失败策略以及一套完整的 JWT Apollo Server 实战接入方案。为什么 GraphQL 需要声明式授权在 express.js 等传统 Node.js 框架中我们通常用passport.js之类的中间件统一完成身份验证与授权。但在 GraphQL 的 resolver 架构里并没有中间件这一层概念每个 field resolver 都需要自己拿 context、自己调用鉴权函数再把用户数据手工传给业务逻辑。这种命令式写法既重复又容易遗漏——新增一个 resolver 时很容易忘记补上鉴权检查从而造成越权漏洞。TypeGraphQL 正是为了解决这个问题把授权做成了框架级特性。整个方案由两部分组成声明层用Authorized装饰器在字段、查询、变更或整个 Resolver 类上标注谁能访问执行层通过buildSchema注册的AuthChecker在运行时统一执行鉴权逻辑并在拒绝访问时按authMode决定是抛出错误还是返回null。从源码看Authorized.ts 是一个支持重载的装饰器工厂它把角色信息收集到 metadata storage 中当装饰器作用在类上时记录为 resolver 级元数据collectAuthorizedResolverMetadata作用在字段/方法上时记录为字段级元数据collectAuthorizedFieldMetadata。这两层元数据最终由 SchemaGenerator 消费——如果存在authorizedFields但未提供authChecker构建 schema 时会直接抛出错误You need to provideauthCheckerfunction forAuthorizeddecorator usage!。声明授权规则Authorized 的三种用法Authorized装饰器可以放在对象类型的字段上也可以放在 Resolver 的查询或变更方法上。字段级保护示例如下ObjectType() class MyObject { Field() publicField: string; Authorized() Field() authorizedField: string; Authorized(ADMIN) Field() adminField: string; Authorized([ADMIN, MODERATOR]) Field({ nullable: true }) hiddenField?: string; }这里有三种典型写法对应不同的角色约束空括号Authorized()只要求用户已通过认证登录不限制具体角色单个或多个字符串角色Authorized(ADMIN)、Authorized(ADMIN, MODERATOR)要求用户具备所列角色中的至少一个数组形式Authorized([ADMIN, MODERATOR])与多参数形式等价角色之间是或的关系。由于装饰器是泛型的角色类型默认是string也可以轻易换成其他类型例如Authorizednumber(1, 7, 22)配合同样泛型的AuthCheckerTContext, TRole使用。这一点可以从 Authorized.ts 的重载签名得到印证AuthorizedRoleType string(roles: readonly RoleType[])与AuthorizedRoleType string(...roles: readonly RoleType[])而角色数组最终通过getArrayFromOverloadedRest统一归一化。字段被拒时的行为差异了解字段保护后还需要理解被拒时响应树的行为差异这在 GraphQL 中非常关键有角色的字段如adminField被无权用户访问时会抛出授权错误且该错误会沿查询树向上传播直到遇到一个可空nullable字段才被吞掉成null声明为nullable的字段如hiddenField被拒时直接返回null而非报错。因此文档中的MyObject对已登录但无ADMIN角色的用户表现为publicField、authorizedField正常读取hiddenField返回nulladminField触发错误并向父级传播。查询与变更方法上的保护Authorized同样可用于 resolver 方法Resolver() class MyResolver { Query() publicQuery(): MyObject { return { publicField: Some public data, authorizedField: Data for logged users only, adminField: Top secret info for admin, }; } Authorized() Query() authedQuery(): string { return Authorized users only!; } Authorized(ADMIN, MODERATOR) Mutation() adminMutation(): string { return You are an admin/moderator, you can safely drop the database ;); } }在该示例中已登录用户可访问publicQuery与authedQuery只有角色包含ADMIN或MODERATOR的用户才能执行adminMutation否则收到授权错误。类级别的授权继承与覆盖如果每个方法都手动加Authorized()不仅繁琐而且容易出错——新加的方法很容易漏标。TypeGraphQL 支持把Authorized()声明在Resolver 类级别一次声明对整个类生效同时允许单个方法覆盖默认规则、收窄权限Authorized() Resolver() class MyResolver { // 继承类级别的鉴权守卫 Query() authedQuery(): string { return Authorized users only!; } // 覆盖类级别的规则为该变更注册需要的角色 Authorized(ADMIN, MODERATOR) Mutation() adminMutation(): string { return You are an admin/moderator, you can safely drop the database ;); } }这里authedQuery继承类上的Authorized()仅要求认证adminMutation则以更严格的角色列表覆盖类级默认值。从源码结构看类级元数据AuthorizedClassMetadata与字段级元数据AuthorizedMetadata在 authorized-metadata.ts 中被分别定义SchemaGenerator 在生成 resolver 执行器时会按字段级优先、否则回退到类级的方式合并角色集合。运行时校验编写 AuthChecker声明完元数据后还需要提供鉴权函数。TypeGraphQL 通过AuthChecker类型把读取用户并判断角色的逻辑完全交给你按业务实现。函数式 AuthChecker最简单的形式是一个普通函数其类型签名见 auth-checker.ts为export const customAuthChecker: AuthCheckerContextType ( { root, args, context, info }, roles, ) { // 从 context 读取用户并对照 Authorized 传入的 roles 参数如 [ADMIN, MODERATOR]检查权限 return true; // 或返回 false 表示拒绝访问 };AuthCheckerFn接收两个参数resolverData: ResolverDataTContextType包含root、args、context、info的标准 resolver 数据对象roles: TRoleType[]来自Authorized装饰器的角色数组即文档中[ADMIN, MODERATOR]这类值。返回值可以是布尔值或Promiseboolean因此鉴权逻辑完全支持异步比如查数据库、调远程权限服务。AuthChecker泛型的第二个参数是RoleType必须与Authorized装饰器的泛型类型保持一致。类式 AuthChecker 与依赖注入如果项目使用了依赖注入容器AuthChecker 也可以写成类实现AuthCheckerInterface见 auth-checker.ts从而在构造函数中注入服务export class CustomAuthChecker implements AuthCheckerInterfaceContextType { constructor( // 依赖注入 private readonly userRepository: RepositoryUser, ) {} check({ root, args, context, info }: ResolverDataContextType, roles: string[]) { const userId getUserIdFromToken(context.token); // 使用注入的服务 const user this.userRepository.getById(userId); // 自定义逻辑例如 return user % 2 0; } }注意这里check方法的roles参数声明为string[]与装饰器的Authorized(ADMIN)角色类型对应若使用非字符串角色两者类型需同步调整。注册 AuthCheckerbuildSchema 配置最后一步是在构建 schema 时把鉴权函数或类注册进去import { customAuthChecker } from ../auth/custom-auth-checker.ts; const schema await buildSchema({ resolvers: [MyResolver], // 注册鉴权函数也可以内联定义 authChecker: customAuthChecker, });authChecker是buildSchema配置对象BuildSchemaOptions中的一个选项除此之外该函数还支持resolvers、emitSchemaFile、authMode等选项最终都会传给底层 SchemaGenerator。需要强调只要存在任何Authorized元数据却没有提供authCheckerschema 构建就会失败。这一约束在 schema-generator.ts 中强制执行避免了声明了权限却无人校验的隐患。authMode静默失败模式如果希望静默的授权守卫、不想向用户返回授权错误可以设置authMode: nullconst schema await buildSchema({ resolvers: [./**/*.resolver.ts], authChecker: customAuthChecker, authMode: null, });此时权限校验失败会返回null而不是抛出授权错误。AuthMode只有两个取值见 auth-checker.tserror默认与null。两者的行为差异在 auth-middleware.ts 中有清晰的实现证据AuthMiddleware会在每次执行 field resolver 前调用鉴权逻辑类式 checker 会通过 IOC 容器实例化当accessGranted为假时——authMode null直接返回nullauthMode error若roles数组为空抛出 AuthenticationErrorextensions 中 code 为UNAUTHENTICATED表示未认证否则抛出 AuthorizationErrorcode 为UNAUTHORIZED表示角色不足。这对前端调试非常友好可以从 GraphQL 响应的extensions.code精确区分没登录和权限不够两种情况。实战配方JWT 认证 Apollo ServerTypeGraphQL 授权机制可以轻松与标准 JWT 方案组合。下面是一套基于apollo/server的完整示例先用express-jwt在 GraphQL 执行前解析令牌再把req.user注入 context最后交给 TypeGraphQL 的授权机制消费。import { ApolloServer } from apollo/server; import { expressMiddleware } from apollo/server/express4; import express from express; import jwt from express-jwt; import bodyParser from body-parser; import { schema } from ./graphql/schema; import { User } from ./User.type; // GraphQL 路径 const GRAPHQL_PATH /graphql; // GraphQL context type Context { user?: User; }; // Express const app express(); // Apollo server const server new ApolloServerContext({ schema }); await server.start(); // 挂载 JWT 或其他认证中间件在 GraphQL 执行前运行 app.use( GRAPHQL_PATH, jwt({ secret: TypeGraphQL, credentialsRequired: false, }), ); // 应用 GraphQL server 中间件 app.use( GRAPHQL_PATH, bodyParser.json(), expressMiddleware(server, { // 构建 context // req.user 来自 express-jwt context: async ({ req }) ({ user: req.user }), }), ); // 启动服务 await new Promisevoid(resolve app.listen({ port: 4000 }, resolve)); console.log(GraphQL server ready at http://localhost:4000/${GRAPHQL_PATH});这套方案的关键点credentialsRequired: false让没有携带令牌的请求也能进入 GraphQL 层再由Authorized决定具体字段是否需要认证——这正好契合部分公开、部分受限的 API 形态context 只携带user后续AuthChecker从 context 读取用户并比对角色即可之后就可以像经典 REST API 一样在 HTTP Header 中使用标准的基于令牌的授权同时享受 TypeGraphQL 的声明式授权机制。完整可运行示例仓库提供了完整的真实世界示例项目 examples/authorization其中各文件与本文概念的对应关系如下auth-checker.ts一个完整的函数式AuthChecker实现了三段式逻辑——无user直接拒绝roles.length 0即Authorized()仅要求认证否则用user.roles.some(role roles.includes(role))检查角色交集recipe.resolver.tsrecipes查询公开、addRecipe变更要求登录、deleteRecipe变更要求ADMIN角色分别对应Authorized()与Authorized(ADMIN)recipe.type.tsingredients字段要求认证、ratings字段要求ADMIN角色直观展示对象类型字段级授权context.type.ts 定义了携带user的 context 类型index.ts 负责组装 schema 并启动服务。该示例的运行方式与仓库其他示例一致基于仓库根目录的package.json安装依赖后以 TypeScript 运行examples/authorization/index.ts即可启动服务随后可通过 Playground 或 GraphQL 客户端在examples/authorization/examples.graphql中查看预置的查询与变更操作。小结TypeGraphQL 的授权体系可以总结为一条清晰的链路Authorized声明权限元数据 → SchemaGenerator 校验并生成鉴权中间件 →AuthChecker在运行时按业务逻辑裁决 →authMode决定失败响应形态。开发者只需在两个地方投入精力决定每个字段/操作的访问规则以及实现一个与业务数据模型对齐的AuthChecker其余样板代码全部由框架接管。配合AuthCheckerInterface类式实现还能无缝接入项目的依赖注入容器让鉴权逻辑与数据库访问、令牌解析等基础设施形成统一闭环。赞分享后端GraphQLAPI设计【免费下载链接】type-graphqlCreate GraphQL schema and resolvers with TypeScript, using classes and decorators!项目地址https://gitcode.com/gh_mirrors/ty/type-graphql点击查看免费下载相关推荐TypeGraphQL 授权机制完全指南用 Authorized 装饰器与 AuthChecker 构建声明式权限控制TypeGraphQL 授权机制完全指南用 Authorized 装饰器与 AuthChecker 构建声明式权限控制 在几乎所有真实业务 API 中限制后端GraphQLAPI设计探索知识图谱的奥秘浙大知识图谱导论课程资源推荐探索知识图谱的奥秘浙大知识图谱导论课程资源推荐 项目介绍 你是否对知识图谱充满好奇想要深入了解这一前沿技术的奥秘浙江大学的知识图谱导论课程资源为你打开了一后端GraphQLAPI设计TypeGraphQL 授权机制完全指南用 Authorized 装饰器与 AuthChecker 实现声明式鉴权TypeGraphQL 授权机制完全指南用 Authorized 装饰器与 AuthChecker 实现声明式鉴权 导读 本文基于 TypeGraphQL后端GraphQLAPI设计上一篇Vision Agent实战指南用自然语言生成视觉AI代码的深度解析下一篇AntiMicroX游戏手柄映射终极指南5分钟让任何手柄玩转PC游戏创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表