资讯中心

PostGraphile 的 PostgreSQL JWT 规范:将 JWT Claims 序列化进数据库会话

📅 2026/9/24 19:18:46
PostGraphile 的 PostgreSQL JWT 规范:将 JWT Claims 序列化进数据库会话
后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载导读本文围绕 PostGraphileCrystal Monorepo 中的 GraphQL 引擎所遵循的PostgreSQL JSON Web Token 序列化规范展开当客户端以 JWT 完成认证后服务端如何把已校验并解码出的 claims 映射到 PostgreSQL 会话从而让行级安全RLS策略和数据库函数基于这些值做授权判断。读完本文你将掌握roleclaim 与jwt.claims.*命名空间的确切映射规则、事务级local语义、在 SQL 中读取 claims 的标准写法以及 PostGraphile 中与该规范配套的pgSettings配置路径与lazy-jwt预设的实现细节。本规范由 Caleb Meredith 为 PostGraphQL 项目撰写语言表述力求通用任何希望把授权逻辑下沉到 PostgreSQL schema 的开发者都可以直接采纳。术语沿用 JSON Web Token 规范RFC 7519的定义。为什么需要一套“JWT → PostgreSQL”序列化规范传统 Web 架构往往在应用层做认证数据库仅被视为存储。自 PostgreSQL 9.5 引入行级安全RLS策略后权限可以下沉到数据层结合 PostgreSQL 基于角色的既有权限体系在表/列权限之上叠加行级约束。当 RLS 开启时所有行默认对除数据库管理员和建表者外的角色不可见权限通过策略选择性授予。要让 RLS 策略和函数感知“当前请求是谁”就必须把认证结果传进数据库会话。JWT 只是众多认证手段之一——PostGraphile 官方并不强制使用 JWT会话 Cookie、API Key、mTLS 等都可行关键是最终把相关信息通过pgSettings暴露给 PostgreSQL。本规范解决的正是在“选择 JWT”的前提下如何标准化地把 claims 序列化到 PostgreSQL 中。:::info[JWT 并非唯一选项] PostGraphile 维护者本人也使用基于 Cookie 的标准会话认证配合 Express/Koa/Fastify 的会话中间件与pgSettings并可借助 passport.js 等成熟认证栈对接 OAuth 社交登录。下文规范描述的是“用 JWT 驱动 PostgreSQL 授权”的其中一种方式。 :::序列化规则两条SET语句JWT 被验证并解码后得到的 claims 将以两种方式序列化进 PostgreSQL 数据库1.roleclaim →SET ROLEroleclaim 对应的角色通过SET ROLE设置set local role $role;其中$role是roleclaim 的值。roleclaim 缺失不算错误——未设置角色时请求照常执行。2. 其余所有 claims →jwt.claims.*命名空间其余每个 claim 都通过SET命令写入jwt.claims命名空间set local jwt.claims.$claim_name to $claim_value;该命令对每一个claim 执行包括iss、sub等注册 claim以及第 1 条中已经用于设置角色的roleclaim。$claim_name是 claim 名$claim_value是关联值。完整示例一个携带以下 claims 的 JWT{ sub: postgraphql, role: user, user_id: 2 }会触发如下 SQLset local role user; set local jwt.claims.sub to postgraphql; set local jwt.claims.role to user; set local jwt.claims.user_id to 2;可以看到role同时满足两个用途既用于SET ROLE也照常写入jwt.claims.role。sub这类注册 claim 同样被放入jwt.claims命名空间方便数据库函数在需要时读取。关于local事务作用域语义规范推荐但不强制使用local修饰符。其含义是每个事务块以BEGIN开始、以COMMIT或ROLLBACK结束拥有自己的局部参数事务提交后这些设置立即失效。演示如下begin; set local jwt.claims.user_id to 2; -- 在此事务内可以访问 jwt.claims.user_id commit; -- 提交后不再能访问 jwt.claims.user_id这一语义对 PostGraphile 尤为重要从 config/overview.mdx 的说明看pgSettings中的数据默认通过set_config($key, $value, true)应用到当前事务true参数即表示仅作用于该事务事务结束时全部自动重置。这与规范中local的建议完全一致保证每个 GraphQL 请求/事务之间不会互相泄漏会话状态。另外注意set_config只接受字符串值因此最佳实践是只向pgSettings喂字符串其他类型会被String()强制转换可能产生非预期效果。在 PostgreSQL 中读取 Claims要读取按本规范序列化进数据库的 claim可以使用current_setting函数或SHOW命令select current_setting(jwt.claims.user_id); -- 或者… show jwt.claims.user_id;在真实 schema 中更常见的写法是加true参数以避免未设置时报错。PostGraphile 测试 schema 即如此实现见 kitchen-sink-schema.sqlcreate function c.current_user_id() returns int as $$ select nullif(current_setting(jwt.claims.user_id, true), )::int; $$ language sql stable;随后该函数可被表默认值、RLS 策略或其他函数引用。RLS 策略也可以直接内联使用current_setting(jwt.claims.user_id, true)来比较行数据与当前请求者身份。从规范到 PostGraphilepgSettings与配置路径pgSettings是 PostGraphile 对接本规范的入口PostGraphile 并不会替你验证 JWT而是通过 pgSettings 配置 把你想让 PostgreSQL 知道的会话信息例如role、jwt.claims.user_id注入数据库事务。其工作方式每个pgService可通过自己的pgSettings回调指定该服务连接数据库时要设置的参数默认的pgSettingsKey是pgSettings通常你会直接在 Grafast 的context回调返回的对象中带上pgSettings键值是“字符串键 字符串值”的纯对象在context回调里务必把args.contextValue?.pgSettings中的既有设置展开合并避免覆盖插件或 adaptor 已加入的值。export default { // ... grafast: { context(requestContext, args) { return { pgSettings: { // 如果已有 pgSettings先合并进来 ...args.contextValue?.pgSettings, // 再加入自己的设置 statement_timeout: 10000, }, }; }, }, };命名约定为什么是jwt.claims.*PostGraphile 对自定义变量有明确命名要求见 config/overview.mdx自定义变量名必须包含一个或两个.且第一个.之前的前缀不能被任何 PostgreSQL 扩展占用。推荐使用jwt.或myapp.前缀例如jwt.claims.userid、myapp.is_admin。不含.的键如role会被当作 PostgreSQL 内部设置原样应用——这正是规范中roleclaim 直接映射SET ROLE的机制。在框架中间件中验证 JWT 并填充 pgSettings你需要在服务端框架Express、Koa、Fastify 等中自行验证 token再把 claims 喂给pgSettings。以 Express 为例参考 jwt-guide.mdx 的完整实现import { PostGraphileAmberPreset } from postgraphile/presets/amber; import { verify } from jsonwebtoken; const preset: GraphileConfig.Preset { extends: [PostGraphileAmberPreset], grafast: { async context(requestContext, args) { const req requestContext.expressv4?.req; const header req?.get(authorization) ?? ; const [, token] header.split( ); const pgSettings { ...args.contextValue?.pgSettings, } as Recordstring, string; if (token) { try { const claims verify(token, process.env.JWT_SECRET!, { audience: postgraphile, algorithms: [HS256], }); if (typeof claims object claims ! null) { if (claims.role typeof claims.role string) { pgSettings.role claims.role; } for (const [key, value] of Object.entries(claims)) { if (typeof value undefined || value null) continue; if (!/^[a-z_][a-z0-9_]*$/i.test(key) || key.length 52) continue; pgSettings[jwt.claims.${key}] String(value); } } } catch (e) { // 让框架决定如何呈现认证错误 requestContext.expressv4?.res?.status(401); throw e; } } return { ...args.contextValue, pgSettings, }; }, }, }; export default preset;关键要点由你决定信任并转发哪些 claim——可以剥离、重命名甚至从会话存储合并数据放进pgSettings的任何值在 RLS 策略执行时都能通过current_setting(..., true)读到不要盲目转发整个 JWT payload只转发数据库 schema 真正需要的部分。这段代码中pgSettings.role claims.role与pgSettings[jwt.claims. key] String(value)正是对上面规范的逐条落实role走SET ROLE语义其余 claims 进入jwt.claims.*命名空间。lazy-jwt预设规范的内置简化实现PostGraphile 提供postgraphile/presets/lazy-jwt便利预设作为临时方案其实现位于 lazy-jwt.ts。启用后它会仅在使用grafservHTTP adaptor 时生效从requestContext.node?.req?.headers?.authorization读取 Bearer token因此只适用于会填充该字段的 adaptor如postgraphile/grafserv/express/v4用共享密钥preset.grafserv.pgJwtSecret或preset.schema.pgJwtSecret通过jsonwebtoken验证 token默认算法HS256/HS384默认 audience 为postgraphile可用preset.grafserv.pgJwtVerifyOptions覆盖将roleclaim 以及符合/^[a-z_][a-z0-9_]*$/i、长度不超过 52 的字母数字 claims 复制进pgSettings的jwt.claims.*命名空间——与规范的序列化规则完全对应。它不处理刷新令牌、密钥轮换、令牌撤销、自定义 claim 映射或多租户密钥查找。官方明确建议仅作为过渡最终应替换为在中间件中显式设置pgSettings的方案参见 config/overview.mdx 的警告。测试辅助代码也印证了 JWT 是测试矩阵的一部分PostGraphile 测试的 snapshot 序列化会识别以jwt开头的字符串值并将其解码展示见 helpers.ts。反向流程从 PostgreSQL 签发 JWT规范定义的是“JWT → PostgreSQL”的写入方向PostGraphile 也支持反向签发把preset.gather.pgJwtTypes中列出的复合类型作为函数返回类型时响应将变成用preset.schema.pgJwtSecret及可选的preset.schema.pgJwtSignOptions签名的 JWT 字符串。v4 兼容预设中jwtPgTypeIdentifier正是映射到pgJwtTypes见 v4.ts 与 v4.ts。注意该功能只负责签名复合 payload不管理刷新令牌也不构成完整认证系统。测试与实现佐证本规范在仓库中有完整的落地证据链规范文档jwt-specification.md本文主体配套指南jwt-guide.mdx 讲解如何在 Express/CLI 场景下填充pgSettings、客户端如何通过 Bearer 头发送 tokenApollo Client 与 Relay 示例以及签发流程配置文档config/overview.mdx 的pgSettings小节与命名约定、current_setting读取示例源码实现lazy-jwt.ts 完整实现了 Bearer token 解析 → claims 校验 →role/jwt.claims.*注入测试 schemakitchen-sink-schema.sql 通过c.current_user_id()函数演示了current_setting(jwt.claims.user_id, true)的实际用法并将该函数用作表默认值。安全与取舍无论采用哪种认证方式PostGraphile 的立场一致认证发生在你的 Web 框架层PostgreSQL 只看到你放进pgSettings的值授权判断则下沉到 RLS 策略与函数中。选择 JWT 时需注意规范的序列化本身不负责 token 校验、过期、撤销、密钥轮换——这些都属于框架层职责建议只在pgSettings中暴露数据库授权真正需要的 claim控制信息暴露面local事务语义与set_config(..., true)的结合能保证请求间状态隔离是生产环境应遵循的默认行为。JWT 只是众多可行方案之一会话 Cookie、OAuth 支撑的 passport 策略、自研 API Key 都能在正确填充pgSettings的前提下达到同等授权效果。选择适合你基础设施的策略并清楚每种方案的取舍。赞分享后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载相关推荐PostGraphile 的 PostgreSQL JWT 规范将 JSON Web Token 序列化到数据库会话的完整指南PostGraphile 的 PostgreSQL JWT 规范将 JSON Web Token 序列化到数据库会话的完整指南 导读 本文详细解读 PostG后端API网关PostGraphile / graphile-build-pg 数据库层安全实践事务级角色与 JWT Claims 注入详解PostGraphile / graphile build pg 数据库层安全实践事务级角色与 JWT Claims 注入详解 导读 本文围绕 graphil后端API网关OptiScaler终极指南跨GPU上采样技术让任何显卡都能享受DLSS级画质OptiScaler终极指南跨GPU上采样技术让任何显卡都能享受DLSS级画质 你是否曾经羡慕Nvidia显卡用户的DLSS技术或者希望你的AMD显卡能获图形学游戏开发创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案