Terminus 中基于 Web5 的 JWT 签名与验证:`cli/pkg/web5/jwt` 包实战指南

发布时间:2026/10/13 2:39:26
Terminus 中基于 Web5 的 JWT 签名与验证:`cli/pkg/web5/jwt` 包实战指南 云原生后端运维【免费下载链接】TerminusOpen-Source Personal Cloud OS for Always-On Agents项目地址https://gitcode.com/gh_mirrors/terminus2/Terminus点击查看免费下载导读在 Terminus 的 CLI 代码库中cli/pkg/web5是承载 DID去中心化标识符与 Web5 密码学能力的核心模块而jwt子包则在其上封装了面向 DID 的 JWT 签发与校验 API。本文以 cli/pkg/web5/jwt/README.md 为骨架结合 jwt.go、jws.go、bearerdid.go 等源码与测试用例完整讲解如何用 Go 为 DID 签发 JWT、如何校验 JWT以及Claims序列化、Purpose/Type选项、过期校验等底层实现细节。读完本文你可以直接在 Terminus 的 CLI 工程中复现完整的“创建 DID → 签发 JWT → 校验 JWT”链路也可以据此理解 Terminus 中基于 DID 的身份签名机制。说明文中所有路径均为当前仓库内的相对路径引用代码以仓库实际内容为准。背景web5包在 Terminus CLI 中的定位在深入jwt之前先理解它所在的模块。cli/pkg/web5是一个完整移植自 Web5 理念的 Go 实现目录结构如下cli/pkg/web5/ ├── crypto/ # 密钥管理与数字签名算法ecdsa / eddsa ├── dids/ # DID 解析、DID Core 数据模型、BearerDID ├── jwk/ # JSON Web Key 编解码 ├── jws/ # JSON Web Signature紧凑格式 JWS ├── jwt/ # 本文主角面向 DID 的 JWT ├── vc/ # 可验证凭证VC / vc-jwt └── pexv2/ # Presentation Exchange v2jwt包本身只包含两个文件与 README 中 Directory Structure 一节完全一致jwt ├── jwt.go # Claims / Decoded / Sign / Verify / Decode └── jwt_test.go # 签名、验证、编解码与畸形输入测试在仓库中jwt包并非孤立存在它被上游的vc包如 vcjwt.go 中调用jwt.Decode与jwt.Decoded.Verify以及 CLI 的 wizard 模块如 user_store.go 中调用jwt.Sign(claims, bearerDID, jwt.Type(JWT))实际使用因此本文讲解的 API 是经过真实调用链验证的。快速开始为 DID 签发一个 JWTREADME 中 Signing 一节的示例代码可直接运行其完整形态如下package main import ( fmt github.com/beclab/Olares/cli/pkg/web5/didjwk github.com/beclab/Olares/cli/pkg/web5/jwt ) func main() { // 1. 创建一个新的 BearerDIDdid:jwk默认 Ed25519 did, err : didjwk.Create() if err ! nil { panic(err) } // 2. 构造 ClaimsIssuer 填入 DID URI私有声明放入 Misc claims : jwt.Claims{ Issuer: did.URI, Misc: map[string]interface{}{c_nonce: abcd123}, } // 3. 签名得到紧凑格式 JWT signedJWT, err : jwt.Sign(claims, did) if err ! nil { panic(err) } fmt.Printf(compact JWT: %s\n, signedJWT) }需要注意两点示例中的didjwk.Create()实际对应仓库中的 didkey/didkey.goREADME 引用的导入路径是历史包名didjwk。Create()默认使用LocalKeyManager并以 Ed25519 生成密钥创建出的BearerDID同时包含 DID 字符串、KeyManager 与 DID Document。claims.Issuer会被jwt.Sign强制覆盖为did.URI。源码中明确写着// iss is required to be equal to the DIDs URI; claims.Issuer did.URI。也就是说签发时无需也无法手工指定其他 issuer这是 DID 语义的一部分——JWT 的签发者必须就是持有该 DID 的主体。测试用例 TestSign_IssuerOverridden 验证了这一点即使传入一个与did.URI不同的 Issuer解码后得到的 Issuer 仍等于did.URI。签发流程在源码中的实际路径jwt.Sign的实现位于 jwt.go其内部流程是解析可选的SignOpt选项Purpose与Type详见下文将claims.Issuer覆盖为did.URIjson.Marshal(claims)得到 payload委托给jws.Sign(payload, did, jwsOpts...)。而jws.Signjws.go进一步完成通过did.GetSigner(o.selector)从 DID Document 中选取一个 verification method 及其私钥用dsa.GetJWA根据公钥类型推导 JWA 算法名Ed25519 对应EdDSAsecp256k1 对应ES256K将kid设为did.Document.GetAbsoluteResourceID(verificationMethod.ID)即${did}#${verificationMethodID}形式的 DID URL以base64url(header) . base64url(payload)为待签数据计算签名最终拼出三段式紧凑 JWT。因此最终产物的 Header 大致为{alg:EdDSA,kid:did:jwk:...#0,typ:}kid携带完整 DID URL这正是验证方得以解析出公钥的关键见下文 Verifying 流程。用Type选项设置typ头README 提到“specifying a specific category of key ... can be done in the same way shown withjws.Sign”。jwt.Sign支持两个选项其中Type(t string)用于设置 JWT 头部的typ字段signedJWT, err : jwt.Sign(claims, did, jwt.Type(JWT))Terminus CLI 的 user_store.go 正是这样调用jwt.Sign(claims, bearerDID, jwt.Type(JWT))的。typ: JWT的意义在于让接收方一眼识别这是一个 JWT 而非普通 JWSvc包在校验 vc-jwt 时也会检查Header.TYP JWT见 vcjwt.go。用Purpose选项选择签名密钥Purpose(p string)用于指定使用 DID Document 中哪个验证关系的密钥来签名例如signedJWT, err : jwt.Sign(claims, did, jwt.Purpose(authentication))不传Purpose时默认使用assertionMethod断言方法。可选的目的值定义在 didcore/document.go 中Purpose 常量值语义PurposeAssertionassertionMethod用于签发断言、可验证凭证默认PurposeAuthenticationauthentication用于登录、挑战-响应等认证场景PurposeCapabilityDelegationcapabilityDelegation用于委托密码学能力PurposeCapabilityInvocationcapabilityInvocation用于调用密码学能力如更新 DID DocumentPurposeKeyAgreementkeyAgreement用于生成加密通信的密钥材料SelectVerificationMethoddocument.go会优先取对应目的列表中第一个 verification methoddid:key创建的 DID Document 将其唯一密钥同时登记到assertionMethod、authentication、capabilityDelegation、capabilityInvocation四种关系中见 didkey.go所以这几种 Purpose 都可以直接使用。校验 JWTVerify与Decoded.VerifyREADME 的 Verifying 示例package main import ( fmt github.com/beclab/Olares/cli/pkg/web5/dids github.com/beclab/Olares/cli/pkg/web5/jwt ) func main() { someJWT : SOME_JWT decoded, err : jwt.Verify(someJWT) if err ! nil { panic(err) } fmt.Printf(verified: %v\n, decoded) }注意 README 中ok, err : jwt.Verify(signedJWT)的写法与仓库实际签名略有出入——当前实现jwt.Verify(jwt string) (Decoded, error)直接返回解码结果与错误校验成功与否通过err nil判断而非布尔返回值。校验的三重检查jwt.Verifyjwt.go先调用Decode解析三段结构再调用Decoded.Verify()。Decoded.Verifyjwt.go按顺序执行过期检查若Claims.Expiration ! 0且time.Now().Unix() Expiration返回JWT has expired签名完整性检查将 JWT 组装为jws.Decoded并调用decodedJWS.Verify()失败返回JWT signature verification failed签发者一致性检查要求Claims.Issuer非空且Header.KID以Claims.Issuer为前缀否则返回JWT issuer does not match the did url provided as KID。其中第 2 步的 JWS 级校验jws.go是核心校验 Header 必须包含alg与kid用dids.Resolve(kid)解析出 DID Document用kid即 DID URL通过SelectVerificationMethod选出对应公钥调用dsa.Verify对parts[0] . parts[1]做签名验证。源码注释还提示了一个值得注意的优化点签发者一致性检查应尽早于网络型 DID 解析执行因为 DID 解析可能触发网络调用见 jwt.go 的 TODO 注释。这属于仓库作者留下的实现约束说明并非当前已实现的行为。错误与Decode的容错Decodejwt.go负责把三段base64url解码为Decoded其容错策略值得注意段数不为 3 →malformed JWT. Expected 3 parts, got NHeader 解码失败、Claims 解码/反序列化失败、Signature 解码失败均返回带malformed JWT前缀的错误用did.Parse(header.KID)解析签名者 DID失败同样报malformed JWT。对应的测试用例覆盖了空串、坏 Header、坏 Signature、kid不是合法 DID 等场景见 jwt_test.go可作为异常处理的参考行为。Decode本身不校验签名、也不校验过期适合仅需解析内容的场景Verify才是完整的校验入口。Claims注册声明与私有声明的扁平化处理Claims结构体jwt.go实现了 RFC 7519 定义的 7 个注册声明字段JSON 键含义RFC 7519Issueriss签发者Subjectsub主体Audienceaud目标受众Expirationexp过期时间Unix 秒NotBeforenbf生效时间Unix 秒IssuedAtiat签发时间Unix 秒JTIjtiJWT 唯一 IDExpiration与NotBefore均为 Unix 时间戳int64这与 JWS 的time时间戳语义一致过期判断由Decoded.Verify依据exp执行。Claims还包含一个特殊字段Misc map[string]any用于承载任意私有声明。README 示例中的c_nonce就通过Misc传入。为保证输出 JSON 是“注册声明 私有声明平铺在顶层”的标准形态Claims自定义了MarshalJSON与UnmarshalJSONjwt.go序列化先以cpyClaims 的拷贝类型避免无限递归序列化注册字段再把Misc中的键值逐一合并进顶层 map反序列化先把原始 JSON 展开为 map凡是iss/sub/aud/exp/nbf/iat/jti之外的键全部归入Misc注册字段再反序列化到cpy。TestClaims_MarshalJSON 与 TestClaims_UnmarshalJSON 验证了Misc中的键值在 JSON 往返中既不丢失、也不会与注册字段冲突。这意味着你可以放心地把自定义字段nonce、业务上下文等放进Misc最终 JWT 的 claims 部分会形如{iss:did:jwk:...,c_nonce:abcd123}谁在使用它vc-jwt 与 CLI 的实际调用为确认这套 API 的实战地位仓库中有两处真实调用vc 包vcjwt.go 用jwt.Decode(vcJWT)解码可验证凭证 JWT随后把iss、jti、sub、exp、nbf等注册声明“反填”进 VC 数据模型对应 W3C VC 数据模型中的 JWT 编码/解码约定最后用jwt.Decoded.Verify()做完整性校验。jwt.Decode的测试用例也使用了真实的 vc-jwt 样例见 jwt_test.go。wizard 模块user_store.go 的SignJWS用jwt.Sign(claims, bearerDID, jwt.Type(JWT))为 DID 用户签名请求载荷并把时间戳写入私有声明。这证明了jwt包不仅存在于 web5 内部还直接支撑着 CLI 的用户认证流程。常见问题与使用注意README 中ok, err : jwt.Verify(...)与当前 API 不一致仓库当前版本Verify返回(Decoded, error)请以源码 jwt.go 为准校验结果以err是否为nil判断。didjwk导入路径已迁移README 与历史文档中的cli/pkg/web5/didjwk在当前仓库中对应 cli/pkg/web5/dids/didkey创建 DID 请使用didkey.Create()。iss强制覆盖jwt.Sign会忽略你手工设置的Issuer这符合“JWT 签发者即 DID 持有者”的语义如需校验签发者一致性Decoded.Verify会检查kid前缀是否匹配iss。Decode与Verify的区别只解析不校验用Decode需要过期与签名双重校验用Verify。网络依赖JWS 级签名校验需要解析 DIDdids.Resolve。对于did:jwk/did:key这类静态 DID解析不依赖网络但设计上该路径是统一的解析入口README 与源码均提示部分 DID 方法如did:dht、did:web的解析器在仓库中仍为占位实现见 resolver.go实际使用时需确认对应 DID 方法的 resolver 已注册。目录结构与设计取舍jwt ├── jwt.go # Claims、Decoded、Sign、Verify、Decode、Marshal/UnmarshalJSON └── jwt_test.go # 覆盖签名、验证、iss 覆盖、claims 编解码、畸形输入README 的 Rationale 一节只写了一句“same asjws”对应的 jws/README.md 给出的设计理由是作者希望获得jws.Sign/jws.Verify这样简洁的 API。落实到代码上jwt与jws的分层是清晰的jwt负责 claims 语义注册声明、私有声明、issuer 约束、过期校验jws负责密码学与紧凑格式jwt.Sign内部委托jws.Signjwt.Decoded.Verify内部委托jws.Decoded.Verify职责单一、便于复用。这种“JWT 建立在 JWS 之上”的结构与 RFC 7519 / RFC 7515 的规范关系一一对应也是阅读与扩展此包时最值得抓住的主线。赞分享云原生后端运维【免费下载链接】TerminusOpen-Source Personal Cloud OS for Always-On Agents项目地址https://gitcode.com/gh_mirrors/terminus2/Terminus点击查看免费下载相关推荐Terminus Web5 Crypto 密码学包实战secp256k1 与 ed25519 的密钥生成、签名与验证Terminus Web5 Crypto 密码学包实战secp256k1 与 ed25519 的密钥生成、签名与验证 Terminus 的 cli/pkg/w云原生后端运维java-jwt 实战指南基于 RFC 7519 的 JWT 创建、签名与验证java jwt 实战指南基于 RFC 7519 的 JWT 创建、签名与验证 java jwt 是 Auth0 出品的 Java 语言 JSON Web T后端认证鉴权OpenCloud 中的 JWT 实战golang-jwt/jwt/v5 签名、验证与安全实践OpenCloud 中的 JWT 实战golang jwt/jwt/v5 签名、验证与安全实践 导读 JSON Web TokenJWT是当前身份认证与授后端微服务存储认证鉴权上一篇三步永久保存微信聊天记录你的数字记忆守护者下一篇Citra模拟器终极优化指南15分钟解决所有运行问题创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询