GraphOS 路由器中的 JWT 验证
限制有凭证用户和系统的访问
此功能只能在 GraphOS 专用或企业计划中使用。
要比较所有计划类型对 GraphOS 功能的支持,请参见 定价页面.
验证对于防止非法访问并保护您的图至关重要。The GraphOS Router支持通过JSON Web Token (JWT) 和 JSON Web Key (JWK) 标准。这种支持与流行的身份提供者(IdP),如 Okta 和 Auth0 兼容。
通过启用 JWT 验证,您可以在边缘阻止恶意流量,而不是依赖于标题转发将令牌传播到您的子图。
💡 提示
你的子图应该始终可访问——只能通过路由器——而不是直接由客户端访问。如果你在路由器中依赖JWT身份验证,这一点尤为重要。有关仅限制子图访问您路由器的步骤,请参阅保护您的子图。
JWT身份验证的工作原理
以下是在GraphOS Router中使用JWT基于身份验证的高级步骤:
每当客户端与您的系统进行身份验证时,您的身份提供者(IdP)都会向该客户端颁发一个有效的JSON Web令牌(JWT)。
在后续请求您的路由器时,经过身份验证的客户端会在指定的HTTP头中提供其JWT。
每当您的路由器收到客户端请求时,它会从指定的头(如果有)中提取JWT。
- 如果没有JWT存在,请求将继续。您可以在稍后的阶段拒绝没有附加JWT的请求(见下文)。
您的路由器使用相应的JSON Web密钥(JWK)验证提取的JWT。
- 您的路由器从其配置文件中指定的URL获取其所有已知的JWK。每个URL在其JWK集中提供其密钥,JWK集是一个名为JWK Set或JWKS)中。
- 如果验证失败,路由器将拒绝请求。这可以发生在JWT格式不正确或过期超过60秒(此窗口考虑了同步问题)的情况下。
路由器从经过验证的JWT中提取所有断言并将其包含在请求的上下文中,使它们可供您的路由器自定义使用,例如Rhai脚本。
您的自定义可以根据提取的断言的详细信息以不同的方式处理请求,或者您可以将断言传播到子图以启用更精细的访问控制。
- 例如,见下文。
开启它
如果您使用自己的自定义IdP,需要高级配置。
否则,如果您通过流行的第三方IdP(Auth0、Okta、PingOne等)颁发JWT,则在您的路由器中启用JWT身份验证是一个两步过程,如下所述。
在路由器的YAML配置文件中设置JWT身份验证的配置选项,在
身份验证
密钥:router.yamlauthentication:router:jwt:jwks: # This key is required.- url: https://dev-zzp5enui.us.auth0.com/.well-known/jwks.jsonissuer: <optional name of issuer>poll_interval: <optional poll interval>headers: # optional list of static headers added to the HTTP request to the JWKS URL- name: User-Agentvalue: router# These keys are optional. Default values are shown.header_name: Authorizationheader_value_prefix: Bearer# array of alternative token sourcessources:- type: headername: X-Authorizationvalue_prefix: Bearer- type: cookiename: authz以下选项的文档如下。
在启动时将以下所有内容传递给
router
可执行文件:- 路由器的YAML配置文件路径(通过
--config
选项) - 路由器应使用的GraphOS变体的图引用(通过
APOLLO_GRAPH_REF
环境变量) - 一个图API密钥,启用路由器通过GraphOS进行认证以获取其超图模式(通过
APOLLO_KEY
环境变量)
APOLLO_GRAPH_REF=docs-example-graph@main APOLLO_KEY="..." ./router --config router.yaml- 路由器的YAML配置文件路径(通过
路由器启动时,会显示一条确认使用了哪些jwks
的日志信息:
2023-02-03T14:05:28.018932Z INFO JWT authentication using JWKSets from jwks=[{ url: "file:///router/jwks.json" }]
配置选项
以下配置选项受支持
选项 | 描述 |
---|---|
| 必需。一群JWT密钥集(JWKS)配置选项:
|
| 客户端请求将使用该HTTP头向路由器提供其JWT的名称。必须是有效的HTTP头名称。 默认值为 |
| 总是先于相应于 默认值为 |
| 这是一个可能的令牌来源数组,因为它们可能根据客户端以不同的头提供,或者可能存储在cookie中。如果如上所述的默认令牌来源定义的 router.yaml
|
| 此选项允许您混合使用 默认情况下,路由器在 当 如果您将 如果您将 默认值是 |
与JWT声明一起工作
在GraphOS Router验证客户端请求的JWT之后,它将该令牌的声明添加到请求的上下文中的此键:apollo_authentication::JWT::claims
- 如果一个客户端请求没有JWT,则这个上下文值是空元组
()
。 - 如果一个JWT存在但JWT验证失败,则路由器拒绝请求。
如果应该拒绝未认证的请求,则可以按以下方式配置路由器:
authorization:require_authentication: true
声明是JWT作用域的个别细节。它们可能包括相关用户的ID、分配给该用户的任何角色以及JWT的过期时间。查看规范。
由于声明被添加到上下文中,您可以定义根据每个请求的声明细节处理每个请求的自定义逻辑。您可以在supergraph服务级别(有关这些选项的更多信息,请参阅路由器自定义)中定义此逻辑。
以下是2个示例Rhai脚本自定义,展示了路由器可以根据请求的声明执行的操作。
示例:将声明作为标题转发到子图
以下是一个示例Rhai脚本,该脚本将JWT的声明通过HTTP头(每个声明一个头)转发到单个子图。这使每个子图能够定义逻辑来处理(或可能拒绝)基于声明细节的 incoming请求。此功能应在您的main.rhai
文件中导入和运行。
ⓘ 注意
此脚本应在 router's SubgraphService
中运行,该服务在 router 向单个 subgraph 发送子查询之前执行。了解关于路由器服务的更多信息。
💡 提示
显式列出声明并始终设置它们的头部,强烈建议这样做,以避免在将头部转发到 subgraph 时可能出现的安全问题。了解有关转发头部的更多信息。。
示例:将声明作为 GraphQL 扩展转发到 subgraph
以下是一个 Rhai 脚本 的示例,该脚本通过 GraphQL 扩展将 JWT 的声明转发到单个 subgraph。这允许每个 subgraph 定义逻辑来处理(或拒绝)基于声明细节的传入请求。此函数应导入到您的 main.rhai
文件中运行。
ⓘ 注意
此脚本应在 router's SubgraphService
中运行,该服务在 router 向单个 subgraph 发送子查询之前执行。了解关于路由器服务的更多信息。
示例:抛出针对无效声明的错误
以下是一个 Rhai 脚本 的示例,该脚本为不同的无效 JWT 声明细节抛出不同的错误。此函数应导入到您的 main.rhai
文件中运行。
ⓘ 注意
此脚本应在 router's SupergraphService
中运行,该服务在 router 开始生成 查询计划 操作之前执行。了解关于路由器服务的更多信息。
示例 main.rhai
为了使用上述 Rhai 示例,您必须像这样将它们导入到您的 main.rhai
中:
通过协处理器进行声明增强
您可能需要比 JSON 网络令牌提供的信息更多的信息。例如,令牌的声明可能包括用户 ID,然后您可以使用这些 ID 来查找用户角色。对于这种情况,您可以使用 协处理器 增强您的 JSON 网络令牌的声明。
创建自己的 JWKS(高级)
ⓘ 注意
- 大多数第三方 IdP 服务会为您创建和托管 JSON Web Key Set (JWKS)。只有在您使用一个自定义的 IdP,并且该 IdP 没有在其路由器可访问的 URL 上发布其 JWKS 的情况下,才阅读此部分。
- 为了与GraphOS Router支持的JWT身份验证兼容,您的 Identity Provider (IdP,或任何向已验证客户端颁发JWT的服务) 必须使用以下之一签名算法,这些算法被路由器支持。
GraphOS Router通过以下地址从您指定的配置选项中获取它使用的每个JSON Web密钥(JWK):jwks
。每个地址必须提供一个包含JWK集(或称为JWKS)的有效JWK的JSON对象。
请查阅您的IdP文档以获取传递给路由器的JWKS URL。
为了向您的路由器提供JWKS,请配置您的IdP服务在其有效JWK集合更改时(例如,当JWK到期或轮换时)执行以下操作:
- 生成一个包含路由器用于执行令牌验证的每个JWK详细信息的有效JWKS对象。
- 将JWKS对象写入路由器可以通过
file://
或https://
URL访问的位置。- ⚠️ 如果任何JWK使用对称签名算法(如
HS256
),则始终使用file://
URL。对称签名算法使用一组共享密钥绝对不应该通过网络访问。
- ⚠️ 如果任何JWK使用对称签名算法(如
💡 提示
确保IdP配置为在JWK集合每次更改时执行这些步骤。
JWKS格式
JWKS是一个具有单级属性的JSON对象:keys
。keys
的值是一个对象数组,每个对象代表单个JWK:
{"keys": [{// These JWK properties are explained below."kty": "RSA","alg": "RS256","kid": "abc123","use": "sig","n": "0vx7agoebGcQSuu...","e": "AQAB"}]}
通常情况下,keys
数组只包含一个JWK,如果您的IdP正在轮换密钥,有时可能包含两个。
JWK对象参考
JWK对象属性分为两大类
通用属性
以下属性适用于任何JWK:
选项 | 描述 |
---|---|
| 该属性表示算法,用于签名或加密使用的算法指示符(标识符),例如HS256、RS256等,并且是大小写不敏感的字符串。 |
RSA 、EC 或oct )。 | 代表算法算法。与JWK一起使用的确切加密算法,包括密钥大小(如 |
| 指代密钥标识符密钥标识符。JWK的唯一标识符。您的身份提供者应在其生成JWK的同时生成每个JWK的 使用特定密钥创建的JWT可以将其标识符包括在其有效载荷中,这有助于路由器确定用于验证的JWK。 |
| 指示JWK的使用方式。规范定义的值是 对于用于JWT身份验证的密钥,此值应为 |
特定算法的属性
RSA
另请参阅JWA规范。
{// Universal properties"kty": "RSA","alg": "RS256","kid": "abc123",// Algorithm-specific properties"n": "0vx7agoebGcQSuu...", // Shortened for readability"e": "AQAB"}
EC(椭圆曲线)
另请参阅JWA规范。
{// Universal properties"kty": "EC","alg": "ES256","kid": "afda85e09a320cf748177874592de64d","use": "sig",// Algorithm-specific properties"crv": "P-256","x": "opFUViwCYVZLmsbG2cJTA9uPvOF5Gg8W7uNhrcorGhI","y": "bPxvCFKmlqTdEFc34OekvpviUUyelGrbi020dlgIsqo"}
选项 | 描述 |
---|---|
| 指示与该公钥一起使用的加密曲线。 规范定义的曲线包括
|
| 该公钥椭圆曲线点的x坐标,作为坐标八位字节表示的base64编码值。 |
| 该公钥椭圆曲线点的y坐标,作为坐标八位字节表示的base64编码值。 |
对称密钥算法(如HMAC)
{// Universal properties"kty": "oct","alg": "HS256","kid": "key1","use": "sig",// Symmetric-algorithm-specific property"k": "c2VjcmV0Cg" // ⚠️ This is a base64-encoded shared secret! ⚠️}
选项 | 描述 |
---|---|
| 共享密钥的值,作为密钥八位字节序列表示的(URL安全,不带填充)base64编码值。 ⚠️ 如果您的JWK使用对称签名算法,总是通过将JWKS提供给路由器的 |
JWK匹配
要匹配传入的JWT与其对应的JWK,路由器会按从高到低的“匹配特定级别”进行匹配,直到确定其JWK集中的第一个兼容JWK:
- JWT和JWK的
kid
和alg
完全匹配。 - JWT和JWK匹配
kid
,且JWT的alg
与JWK的kty
兼容。 - JWT和JWK的
alg
完全匹配。 - JWT的
alg
与JWK的kty
兼容。
这种匹配策略是必要的,因为某些身份提供者(IdP)在它们的JWKS中未指定alg
或kid
值。然而,它们总是指定一个kty
,因为该值由JWK规范要求。
转发JWT到子图
由于GraphOS路由器处理验证传入JWT,您很少需要将整个JWT传递到单个subgraphs。相反,您通常希望将JWT 断言 传递到子图以实现精细粒度的访问控制。
如果您确实需要将整个JWT传递到subgraphs,您可以通过GraphOS路由器的一般用途HTTP头传播设置来实现。
可观测性
如果您的router启用了追踪,JWT身份验证插件有自己的追踪跨度:authentication_plugin
如果您的router启用了通过Prometheus的指标收集,JWT身份验证插件提供并导出了以下指标:
apollo_authentication_failure_count
apollo_authentication_success_count
这些指标具有以下形态
# HELP apollo_authentication_failure_count apollo_authentication_failure_count# TYPE apollo_authentication_failure_count counterapollo_authentication_failure_count{kind="JWT",service_name="apollo-router"} 1# HELP apollo_authentication_success_count apollo_authentication_success_count# TYPE apollo_authentication_success_count counterapollo_authentication_success_count{kind="JWT",service_name="apollo-router"} 11