微服务认证与授权: 11 — JWT 签名、校验

发布时间:2026/7/9 13:13:20

微服务认证与授权: 11 — JWT 签名、校验 11 — JWT 签名、校验 GitHub: https://github.com/geekchow/micro-service-auth本 PoC 中令牌如何被签名、校验与内省 —— 以及为什么即便对一个结构合法、尚未过期的 JWTKeycloak也可能返回active: false。全文使用的固定术语见 01 — 概念KeycloakIdP、KongPEP、OPAPDP、banking-api-service资源服务器。四个要素1. 签名签名是「Keycloak创建了该令牌」的密码学证明。它是在Keycloak签发访问令牌时用Keycloak的私钥对令牌的 header 与 payload 签名而产生的。为什么重要它证明令牌来自Keycloak它证明 payload 在签发后未被改动它防止调用方仅通过 Base64URL 编码数据就伪造出一个看似有效的假令牌2. 校验Validation校验是指检查令牌是真实的、且对本次请求是可接受的。典型的校验项有签名有效令牌未过期exp签发者iss与期望的Keycloakrealm 匹配受众aud与该 client 或 API 匹配在本 PoC 中banking-api-service使用 JWKS 在本地校验 JWTKong在信任令牌做策略决策之前使用Keycloak内省3. 内省Introspection内省是从某个服务向Keycloak内省端点发起的一次实时调用。Keycloak回复令牌是否active也可能返回令牌元数据。为什么重要它从「真相之源」确认令牌仍然有效它能捕获被吊销的或其他 inactive 的令牌相比仅解码 JWT它给出更强的信任检查在本 PoC 中Kong在用解码后的声明为OPA构建策略 input 之前先对令牌做内省。关于「为什么即便对一个结构合法、未过期的 JWTKeycloak也可能返回active: false」的完整机制详见下文「内省实际检查什么」一节。4. 声明Claims声明是 JWT payload 内部的数据。本 PoC 中的示例issaudpreferred_usernamerealm_access.rolescustomer_idaccount_ids解码令牌让你能读取这些声明。校验或内省告诉你这些声明是否可信。信任链如何工作Keycloak issues JWTHeader payload signedKeycloak private keyJWT returned to aliceService decodes payload to read claimsbanking-api-service validates signature via JWKSKong introspects token with KeycloakClaims can be trusted关键观念是解码与信任不是一回事。解码回答令牌说了什么校验回答它真的由Keycloak签发吗它现在可接受吗内省回答Keycloak是否仍认为该令牌处于 active签名验证如何工作JWT 有三部分header.payload.signatureKeycloak用其私钥对header.payload部分签名。签名作为第三段附加在后面。当服务校验令牌时它做相反的过程把 JWT 拆成 header、payload 与 signature获取或加载Keycloak的公钥通过 JWKS —— 见 12 — JWKS 深入解析从 header 与 payload 重新计算期望的签名将计算出的签名与令牌中的签名比较若二者匹配则令牌是用配对的私钥签名的且 payload 未被改动。若不匹配则令牌被拒绝。私钥与公钥Keycloak把私钥保密。服务从不需要私钥。它们只需要公钥而公钥是可以安全共享的。正是这把公钥让banking-api-service与其他服务能够验证某个令牌来自期望的Keycloakrealm。本 PoC 中的密码学机制本 PoC 中的访问令牌使用RS256R RSA 公钥密码学S 签名signature256 SHA-256 哈希签名过程Keycloak构建 JWT 的 header 与 payload。它对每一部分做 Base64URL 编码。它把它们拼接为header.payload。它用 SHA-256 对该字符串哈希。它用Keycloak的 RSA 私钥对哈希签名。它把得到的签名作为第三段附加到 JWT 上。当服务校验令牌时它反过来把 JWT 拆成 header、payload 与 signature对 header 与 payload 做 Base64URL 解码重建header.payload签名输入用Keycloak的 RSA 公钥验证签名若签名是用配对的私钥创建的验证成功。若令牌被修改过验证失败因为哈希不再与被签名的内容匹配。用大白话说Keycloak用其私钥签名服务用Keycloak的公钥验证SHA-256 哈希让 payload 一旦被篡改即可被发现服务如何在 JWKS 端点发现并缓存Keycloak的公钥见 12 — JWKS 深入解析。校验通常检查什么校验比签名检查更宽泛。服务通常检查签名有效exp未过iss与期望的 realm 匹配aud与期望的 client 或 API 匹配令牌类型符合服务的预期若其中任一检查失败该令牌不应被信任用于授权决策。在本 PoC 中banking-api-service对传入请求执行 JWT 校验因此它能拒绝过期的、格式错误的、或为错误受众签发的令牌。内省实际检查什么本节是本系列文档中对内省机制的唯一完整论述。内省时 Keycloak 做什么当Kong调用Keycloak的内省端点时Keycloak做的不止是解码 JWT。在高层面上Keycloak检查令牌能否被解析令牌在密码学上是否可接受令牌是否过期用户会话是否仍处于 active客户端会话是否仍处于 active是否有登出、吊销或失效事件使该令牌不可用若这些检查通过Keycloak返回{active:true}若不通过Keycloak返回{active:false}所以内省回答的是Keycloak此刻是否仍认为该令牌可用为什么即便对结构合法、未过期的 JWTKeycloak 也可能返回active: false这是整个技术栈中最重要的观念之一。因为Keycloak保有服务端会话状态所以即便满足以下条件它也能说一个令牌是 inactive令牌看起来仍是一个有效的 JWT声明仍能被解码令牌尚未到达其exp时间戳原因在于Keycloak不仅是该 JWT 的签发者 —— 它还是该 JWT 背后实时会话状态的拥有者。Keycloak可能返回active: false的例子alice登出了用户会话过期了客户端会话过期了alice账户被禁用了该 client如mobile-banking-app被禁用了发生了 realm 或 client 的失效事件所以当Keycloak收到一个内省请求时它不仅在问「这个 JWT 看起来格式良好吗」—— 它还在问「依据Keycloak此刻的状态这个令牌是否仍属于一个存活且可接受的会话」JWT 携带令牌数据。Keycloak保有会话真相。这是关于内省最重要的思维模型。Keycloak 如何保有会话状态访问令牌与会话相关但不是同一回事。JWT 携带身份与声明数据Keycloak在服务端保有实时会话信息Keycloak通常维护以下会话层层它代表什么认证会话登录流程期间的临时状态登录完成或过期后移除用户会话某个 realm 中被认证的用户会话 —— 跟踪开始时间、空闲/过期、登出状态客户端会话该用户会话中按 client 划分的参与例如对mobile-banking-app运行时Keycloak主要把在线会话状态存放在 Infinispan 缓存中。离线会话则持久化在数据库中。在集群部署里缓存在各节点间复制。实践要点令牌声明随 JWT 携带但实时会话活动维护在服务端。这正是为什么一个令牌可以被正确解码但若其背后的会话状态已不存在内省仍可能返回active: false。本 PoC 中的内省流程banking-api-serviceOPAKeycloakKongalicebanking-api-serviceOPAKeycloakKongaliceAPI request bearer tokenPOST /introspect (token)check cryptography session stateactive: true or active: falsepolicy input (claims from token)allow or denyforward allowed requestvalidate JWT signature issuer audienceAPI response会话与令牌的关系alice loginKeycloakServer-side session stateAccess token and refresh tokenKong introspectionbanking-api-service JWT validationJWKS vs 内省 —— 该用哪个本 PoC 两种机制都用但用在不同的地方。为什么banking-api-service用 JWKS 而非内省banking-api-service充当资源服务器。它用 JWKS 做 JWT 校验理由如下1. 本地且快速。banking-api-service从 JWKS 端点下载Keycloak的公钥后便能在本地校验 JWT每个请求都无需网络往返。2. JWT 本就为此模式而设计。一个自包含的 JWT 已经携带了声明、过期、签发者、受众与签名。这使得本地 JWKS 校验成为 Spring Security JWT 资源服务器自然且标准的模式。3. 更好的韧性。若Keycloak短暂变慢或不可用只要服务已经拥有所需公钥对已签发的令牌做本地 JWT 校验仍可工作。每个请求都内省会对Keycloak的实时可用性形成硬依赖。4. 更清晰的资源服务器边界。Spring Security 的资源服务器支持正是为「bearer JWT 通过 JWKS 发现公钥 签发者与受众校验」而设计的。使用 JWKS 是标准、高效的模型 —— 而非权宜之计。密钥轮换机制与 JWKS 缓存见 12 — JWKS 深入解析。每种机制回答什么机制回答的问题JWKS 校验该令牌是否由受信任的签发者签名它是否有正确的iss、aud与exp内省Keycloak此刻是否仍认为该令牌处于 activeJWKS 校验回答令牌在密码学上是否可信。内省再补上一个实时答案令牌背后的会话是否仍存活。为什么本 PoC 两者都用组件机制用途Kong内省边缘的实时令牌活跃性检查 —— 在构建OPA策略 input 之前向Keycloak询问令牌是否 activebanking-api-serviceJWKS 校验本地密码学验证 —— 服务不盲目相信网关已经检查了一切纵深防御即便Kong已经内省过banking-api-service仍把 JWT 校验作为自己的信任边界。简短版内省 实时会话状态检查JWKS 校验 本地密码学信任检查声明用来做什么声明携带身份与授权上下文。示例iss告诉服务令牌由谁签发aud告诉服务令牌是发给哪个 client 或 API 的preferred_username给出可读的用户名alice、ops-adminrealm_access.roles给出角色信息customer_id与account_ids携带业务上下文声明一经解码就很容易读取但只有在校验或内省之后才可以安全地据其行动。端到端示例alicealice向Keycloak认证。Keycloak在服务端创建一个用户会话与一个客户端会话。Keycloak签发一个签名的 JWT 访问令牌包含iss、aud、preferred_username、customer_id与account_ids。alice把 bearer 令牌发给Kong。Kong调用Keycloak的内省端点 ——Keycloak检查 JWT 与实时会话状态返回active: true。Kong把策略 input解码后的声明发给OPAOPA返回 allow。Kong把请求转发给banking-api-service。banking-api-service用Keycloak的公钥经 JWKS 获取校验 JWT 签名检查iss、aud与exp。banking-api-service把 API 响应返回给alice。两层信任实时的Keycloak会话验证经由Kong内省本地的密码学验证经由banking-api-service的 JWKS 校验关于访问令牌如何通过刷新令牌续期见 13 — 访问令牌与刷新令牌的生命周期。小结解码decode 读出令牌说了什么校验validate 证明令牌是真实的、是发给本服务的、且未过期内省introspect 询问Keycloak令牌的会话是否仍存活签名让令牌一旦被篡改即可被发现。校验让令牌在本地可被接受。内省给出来自Keycloak的实时答案 —— 而Keycloak即便对一个结构合法、未过期的 JWT 也能返回active: false因为它拥有的是会话真相而不仅仅是令牌字符串。← Prev: 10 — identity-bootstrap-service · Next: 12 — JWKS 深入解析 →

相关新闻