网络安全认证鉴权后端【免费下载链接】joseJWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes项目地址https://gitcode.com/gh_mirrors/jo/jose点击查看免费下载导读UnsecuredJWT是 jose 库中用于处理Unsecured无签名、无加密JWT的工具类这类 JWT 的受保护头部固定为{ alg: none }通常用于纯声明传递、临时票据、内部调试或对安全要求极低的应用场景。本文将以官方文档 docs/jwt/unsecured/classes/UnsecuredJWT.md 为骨架结合仓库源码 src/jwt/unsecured.ts 与测试用例完整讲解该类从构造、声明设置、编码到解码验证的全流程并深入剖析其底层校验逻辑、时间跨度解析规则与典型错误场景。读完本文你将能够熟练使用UnsecuredJWT完成无签名 JWT 的生产与消费并理解其与常规签名 JWT 在安全性上的本质差异。一、UnsecuredJWT 是什么根据 jose 官方 API 文档的定义UnsecuredJWT类是一个用于处理{ alg: none }Unsecured JWT 的实用工具。所谓 Unsecured即令牌既不签名也不加密它的三段式紧凑序列中第三段签名段为空字符串整体结构为header.payload.注意末尾的点号后没有任何签名内容。该类的定位可以从源码的模块注释中得到印证见 src/jwt/unsecured.tsUnsecured (unsigned unencrypted) JSON Web Tokens (JWT)导出方式UnsecuredJWT以命名导出的方式从主模块入口jose导出同时也从子路径导出jose/jwt/unsecured提供。在仓库 src/index.ts 中可以看到这两行导出声明export { UnsecuredJWT } from ./jwt/unsecured.js export type { UnsecuredResult } from ./jwt/unsecured.js因此你可以用以下任一方式引入import { UnsecuredJWT } from jose // 或 import { UnsecuredJWT } from jose/jwt/unsecured适用场景与安全警告无签名 JWT 意味着任何拿到令牌的人都可以随意篡改 payload 并重新编码它不提供任何完整性保护。因此它只适合传递无需防篡改的公开声明例如urn:example:claim: true这样的只读标记作为示例、教学或调试过程中的临时令牌下游系统已通过其他通道如 TLS 之外的业务上下文确认可信的场景。切勿用它承载敏感数据、身份认证或授权决策。jose 官方文档将此类令牌明确定义为 Unsecured正是为了强调这一点。二、编码Encode构造与声明链式设置构造函数new UnsecuredJWT(payload?)payload?JWTPayload类型即 JWT Claims Set 对象默认值为空对象{}。源码中构造函数实际继承自JWTClaimsBuilder见 src/jwt/unsecured.ts其实现位于 src/lib/jwt_claims_set.tsexport class JWTClaimsBuilder { constructor(payload: types.JWTPayload {}) { if (!isObject(payload)) { throw new TypeError(JWT Claims Set MUST be an object) } ;(producerPayloads || new WeakMap()).set(this, structuredClone(payload)) } // ... }需要注意两点实现细节传入的 payload 必须是普通对象否则抛出TypeError(JWT Claims Set MUST be an object)payload 会通过structuredClone进行深拷贝存入WeakMap之后再调用链式 setter 修改的是副本不会污染调用方传入的原始对象。编码方法encode()encode(): stringencode()将当前 Claims Set 编码为紧凑形式的 Unsecured JWT 字符串。源码src/jwt/unsecured.ts非常直白encode(): string { const header b64u.encode(JSON.stringify({ alg: none })) const payload b64u.encode(jwtData(this)) return ${header}.${payload}. }即头部固定为{ alg: none }与 payload 分别做 base64url 编码用.拼接最后签名段为空。jwtData见 src/lib/jwt_claims_set.ts在序列化前还会校验iat、nbf、exp三个时间声明若为数字则必须是有限数Number.isFinite。完整的编码示例官方文档给出的编码示例已转换为从仓库根目录出发的完整上下文const unsecuredJwt new jose.UnsecuredJWT({ urn:example:claim: true }) .setIssuedAt() .setIssuer(urn:example:issuer) .setAudience(urn:example:audience) .setExpirationTime(2h) .encode() console.log(unsecuredJwt)在测试 test/jwt/unsecured.test.ts 中new UnsecuredJWT({ urn:example:claim: true }).encode()的产物被固定为eyJhbGciOiJub25lIn0.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZX0.解码这三段可以看到头部为{alg:none}payload 为{urn:example:claim:true}签名段为空。另外new UnsecuredJWT().encode()空 payload的输出是eyJhbGciOiJub25lIn0.e30.其中e30.正是空对象{}的 base64url 编码见 test/jwt/unsecured.test.ts。链式声明设置方法以下 setter 均返回this支持链式调用。它们定义在JWTClaimsBuilder基类中src/lib/jwt_claims_set.ts每个方法都会先做类型校验再写入 payload。方法对应 Claims参数类型校验规则setIssuer(issuer)issIssuerstring非字符串抛TypeErrorsetSubject(subject)subSubjectstring非字符串抛TypeErrorsetAudience(audience)audAudiencestring \| string[]必须是字符串或全为字符串的数组setJti(jwtId)jtiJWT IDstring非字符串抛TypeErrorsetIssuedAt(input?)iatIssued Atstring \| number \| Date可省略省略时取当前时间戳setExpirationTime(input)expExpiration Timestring \| number \| Date见下文时间规则setNotBefore(input)nbfNot Beforestring \| number \| Date见下文时间规则对应的类型定义见JWTPayloadaud?、exp?、iat?、iss?、jti?、nbf?、sub?均为可选成员且允许携带任意其他自定义成员。时间声明iat / exp / nbf的输入规则这是本类最值得细读的部分。三个时间 setter 接受三种输入官方文档 UnsecuredJWT.md 有完整说明number直接作为 Unix 时间戳秒使用Date转换为 Unix 时间戳Math.floor(date.getTime() / 1000)string解析为相对当前 Unix 时间戳的时间跨度。其中字符串的解析实现位于 src/lib/jwt_claims_set.ts核心正则与换算如下const REGEX /^(\|\-)? ?(\d|\d\.\d) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i const multipliers { s: 1, m: 60, h: 3600, d: 86400, w: 604800, y: 31557600 }关键规则总结格式数字后跟单位例如5 minutes、1 day合法单位拼写sec/secs/second/seconds/sminute/minutes/min/mins/mhour/hours/hr/hrs/hday/days/dweek/weeks/wyear/years/yr/yrs/y正则忽略大小写且只取首字符映射到乘数月份不受支持一年按 365.25 天计算乘数y: 31557600 365.25 × 86400减法前置-如-10s或后缀ago如10s ago都会让结果取负即时间往过去偏移from now 后缀10s from now仅用于可读性语义就是加到当前时间戳上前置如10s同样合法单位缺省如10或月份month会抛出TypeError(Invalid time period format)。numericDate的统一换算逻辑src/lib/jwt_claims_set.ts为function numericDate(value: number | string | Date, label: string) { if (typeof value number) return validateInput(label, value) if (value instanceof Date) return validateInput(label, epoch(value)) return epoch(new Date()) secs(value) }即字符串输入最终都是当前Unix时间戳 secs(字符串)。测试 test/jwt/time_setters.ts 覆盖了10s、10s、-10s、 10s、- 10s、10s from now、10s ago、new Date(now * 1000)、数字0等多种输入向量并通过 test/jwt/unsecured.test.ts 对setIssuer、setSubject、setAudience、setJti、setIssuedAt、setExpirationTime、setNotBefore七个方法逐一断言其写入的声明值与期望值一致。setIssuedAt()还有一个特例不传参数时直接使用当前时间戳见 src/lib/jwt_claims_set.tssetIssuedAt(value?: number | string | Date): this { const payload producerPayload(this) if (value undefined) { payload.iat epoch(new Date()) } else if (typeof value string) { payload.iat validateInput(setIssuedAt, epoch(new Date()) secs(value)) } else { payload.iat numericDate(value, setIssuedAt) } return this }三、解码Decode静态方法与 Claims 校验静态方法签名static decodePayloadType JWTPayload(jwt: string, options?: JWTClaimVerificationOptions): UnsecuredResultPayloadTypejwt要解码的 Unsecured JWT 字符串options?JWTClaimVerificationOptionsJWT Claims Set 验证选项泛型PayloadType默认JWTPayload用于声明你期望 payload 携带的类型返回值UnsecuredResult包含payloadJWT Claims Set与headerJOSE 头部对 Unsecured JWT 恒为{ alg: none }。官方文档的解码示例const { payload, header } jose.UnsecuredJWT.decode(unsecuredJwt, { issuer: urn:example:issuer, audience: urn:example:audience, }) console.log(header) console.log(payload)解码底层实现与防御性校验decode的实现位于 src/jwt/unsecured.ts其校验链条非常严密共分五步第一步入参类型检查if (typeof jwt ! string) { throw new JWTInvalid(Unsecured JWT must be a string) }第二步紧凑结构检查const { 0: encodedHeader, 1: encodedPayload, 2: signature, length } jwt.split(.) if (length ! 3 || signature ! ) { throw new JWTInvalid(Invalid Unsecured JWT) }必须是三段且第三段为空字符串。测试中的....、..、..foo以及带签名段的eyJhbGciOiJIUzI1NiJ9...都会命中此分支见 test/jwt/unsecured.test.ts。第三步头部解析与 JWS 选项校验头部经parseJoseHeader解析后会依次执行validateCritcrit 扩展参数识别与validateB64b64 参数校验。任何JWSInvalid都会被包装为JWTInvalid(Invalid Unsecured JWT, { cause })抛出因此调用方看到的是统一错误码ERR_JWT_INVALID且可通过error.cause查看底层的ERR_JWS_INVALID详情。测试 test/jwt/unsecured.test.ts 覆盖了crit为 null、crit缺少对应扩展参数、b64类型错误等多种头部畸形场景。第四步算法与编码方式硬约束if (header.alg ! none) { throw new JWTInvalid(Invalid Unsecured JWT) } if (!b64) { throw new JWTInvalid(JWTs MUST NOT use unencoded payload) }头部alg必须是none否则拒绝解码如测试中传入alg: HS256的令牌禁止使用b64: false的未编码 payload 形式——这正是 RFC 7797同时带有未被识别的crit扩展头参数的令牌会被拒绝并抛出ERR_JOSE_NOT_SUPPORTED见 test/jwt/unsecured.test.ts。第五步Claims Set 解析与选项验证payload 段先 base64url 解码失败会抛JWTInvalid(Failed to base64url decode the payload)随后交给validateClaimsSetsrc/lib/jwt_claims_set.ts执行完整的 Claims 验证。payload 必须是顶层 JSON 对象否则抛JWTInvalid(JWT Claims Set must be a top-level JSON object)。验证选项详解JWTClaimVerificationOptionsdecode的第二个参数即JWTClaimVerificationOptions所有字段均为可选选项类型作用issuerstring \| string[]期望的iss值设置后强制要求iss声明存在subjectstring期望的sub值设置后强制要求sub声明存在audiencestring \| string[]期望的aud值可多个设置后强制要求aud声明存在requiredClaimsstring[]必须存在的额外声明名列表maxTokenAgestring \| number从iat起算的最大存活时间秒或时间跨度字符串设置后强制要求iat存在clockTolerancestring \| number时钟偏移容忍秒或时间跨度字符串用于nbf、exp及maxTokenAge下的iat比较currentDateDate比较 NumericDate 时使用的当前时间默认new Date()typstring期望的typ头部参数值设置后强制要求头部携带typ底层实现要点src/lib/jwt_claims_set.ts存在性检查requiredClaims加上由maxTokenAge/audience/subject/issuer选项隐式引入的iat/aud/sub/iss构成必须存在的声明集合缺失即抛JWTClaimValidationFailedmissing原因值匹配iss支持数组任一匹配aud支持payload 的 aud 数组包含任一期望值的包含式匹配checkAudiencePresence见 src/lib/jwt_claims_set.tssub要求严格相等不匹配抛check_failed原因typ归一化比较时会把typ小写化并允许JWT与application/JWT互相等价normalizeTypsrc/lib/jwt_claims_set.tsnbfnbf now tolerance时抛JWTClaimValidationFailedexpexp now - tolerance时抛JWTExpired即ERR_JWT_EXPIREDmaxTokenAgenow - iat - tolerance max抛JWTExpirediat过旧now - iat -tolerance抛JWTClaimValidationFailediat在未来三个时间声明iat/nbf/exp若存在但不是数字会以invalid原因抛JWTClaimValidationFailed。相关错误类型都定义在 src/util/errors.tsJWTInvalid第 418 行起、JWTExpired第 196 行起、JWTClaimValidationFailed第 135 行起它们统一继承自JOSEError基类可通过error.code获取形如ERR_JWT_INVALID、ERR_JWT_EXPIRED、ERR_JWT_CLAIM_VALIDATION_FAILED的错误码便于上层做分类处理。类型化解码由于decode是泛型方法你可以为 payload 声明具体类型以获取完整的 TypeScript 推导interface MyClaims { urn:example:claim: boolean sub?: string } const { payload, header } UnsecuredJWT.decodeMyClaims(token, { issuer: urn:example:issuer, }) // payload 的类型为 MyClaims JWTPayload解码结果UnsecuredResultPayloadType的完整结构见 docs/jwt/unsecured/interfaces/UnsecuredResult.mdpayload为PayloadType JWTPayload的交叉类型header恒为JWSHeaderParameters对 Unsecured JWT 总是{ alg: none }。四、测试覆盖从测试用例反推行为契约仓库在 test/jwt/unsecured.test.ts 中对本类做了系统性的行为验证除前文已引用的场景外还包括空 payload 编码new UnsecuredJWT().encode()产出eyJhbGciOiJub25lIn0.e30.非法输入矩阵null、....、..、..foo、非none算法的令牌、非 base64url 的 payload 段全部断言抛出ERR_JWT_INVALID及对应错误消息crit 与 b64 扩展拒绝未知 crit 扩展、非法 crit 结构、非法 b64 类型同时允许b64: false/b64: true/b64: false这些在非 JWT 上下文中合法、但在 Unsecured JWT 中会被安全处理的头部变体注意只要b64不为显式false即视为编码正常七个链式 setter 的完整向量测试通过 test/jwt/time_setters.ts 提供的输入矩阵逐一验证写入正确性。这些测试证明了UnsecuredJWT的一个核心设计态度对任何不符合 Unsecured JWT 严格定义的输入一律拒绝绝不静默降级。五、与其他模块的对照何时不该使用 UnsecuredJWTjose 仓库中与之形成对照的是签名与加密路径需要完整性保护时应改用CompactSigncompactVerify或 Flattened / General 变体并通过jwtVerify校验签名后消费 JWT需要机密性保护时应使用EncryptJWTjwtDecryptUnsecuredJWT只应在无篡改风险这一前提明确成立时使用。文档中反复出现的{ alg: none }定义正是 JOSE 规范对这类令牌的标准化称呼。六、完整实战示例将编码与解码串联起来一个完整的可运行示例Node.js / Deno / Bun 等支持 Web API 的环境均可import { UnsecuredJWT } from jose // —— 编码 —— const token new UnsecuredJWT({ urn:example:claim: true }) .setIssuedAt() .setIssuer(urn:example:issuer) .setAudience([urn:example:audience-a, urn:example:audience-b]) .setExpirationTime(2h) .setNotBefore(-5 minutes) // 允许最多 5 分钟的签发时钟偏移 .setJti(550e8400-e29b-41d4-a716-446655440000) .encode() console.log(token) // header.payload.无签名段 // —— 解码 校验 —— try { const { payload, header } UnsecuredJWT.decode(token, { issuer: urn:example:issuer, audience: [urn:example:audience-a, urn:example:audience-b], clockTolerance: 5 minutes, // 容忍签发方与消费方之间的时钟偏移 }) console.log(header) // { alg: none } console.log(payload) // 包含 iat / iss / aud / exp / nbf / jti 及自定义声明 } catch (err) { if (err.code ERR_JWT_EXPIRED) { console.error(令牌已过期) } else if (err.code ERR_JWT_CLAIM_VALIDATION_FAILED) { console.error(Claims 校验失败, err.claim, err.reason) } else { console.error(无效的 Unsecured JWT, err.code, err.message) } }七、小结与最佳实践UnsecuredJWT将{ alg: none }无签名 JWT 的编码与解码封装为一组类型安全、校验严密的 API。使用时的最佳实践建议明确安全边界仅在无篡改风险或完整性由外部机制保障的场景使用充分利用验证选项解码时传issuer、audience、requiredClaims、maxTokenAge、clockTolerance等选项把 Claims 校验交给 jose而不是手工拼 JSON善用错误码基于ERR_JWT_INVALID、ERR_JWT_EXPIRED、ERR_JWT_CLAIM_VALIDATION_FAILED等错误码做分类处理并通过error.cause定位底层ERR_JWS_INVALID细节时间跨度字符串要精确牢记单位集合秒/分/时/天/周/年、ago与-表示过去、年按 365.25 天换算且不支持月。如需深入了解相关类型与选项可继续阅读仓库中的 docs/jwt/unsecured/interfaces/UnsecuredResult.md、docs/types/interfaces/JWTClaimVerificationOptions.md 与 docs/types/interfaces/JWTPayload.md实现细节可对照 src/jwt/unsecured.ts 与 src/lib/jwt_claims_set.ts测试契约可参考 test/jwt/unsecured.test.ts。赞分享网络安全认证鉴权后端【免费下载链接】joseJWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes项目地址https://gitcode.com/gh_mirrors/jo/jose点击查看免费下载相关推荐抖音批量下载器 douyin-downloader 存储层深度解析SQLite 去重历史、异步文件管理与元数据落盘抖音批量下载器 douyin downloader 存储层深度解析SQLite 去重历史、异步文件管理与元数据落盘 本指南围绕 douyin download网络安全认证鉴权后端TDengine 硬件故障排查手册内存、硬盘、RAID 控制器与文件系统诊断实战TDengine 硬件故障排查手册内存、硬盘、RAID 控制器与文件系统诊断实战 适用场景TDengine 数据库报出数据校验失败如 block chec网络安全认证鉴权后端Kimi Code CLI 插件系统实战用 plugin.json 打造轻量级自定义工具Kimi Code CLI 插件系统实战用 plugin.json 打造轻量级自定义工具 Kimi Code CLI 的插件Plugin系统允许你通过一个网络安全认证鉴权后端上一篇终极免费OCR神器Umi-OCR离线文字识别完全指南下一篇OpenCore Legacy Patcher终极指南让老Mac免费运行最新macOS系统的完整解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
