这是《冷饭新炒》系列的第五篇文章。
本文会翻炒一个用以产生访问令牌的开源标准JWT,介绍JWT的规范、底层实现原理、基本使用和应用场景。
JWT规范很可惜维基百科上没有搜索到JWT的条目,但是从jwt.io的首页展示图中,可以看到描述:
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties
从这段文字中可以提取到JWT的规范文件RFC 7519,里面有详细地介绍JWT的基本概念,Claims的含义、布局和算法实现等,下面逐个展开击破。
JWT基本概念JWT全称是JSON Web Token,如果从字面上理解感觉是基于JSON格式用于网络传输的令牌。实际上,JWT是一种紧凑的Claims声明格式,旨在用于空间受限的环境进行传输,常见的场景如HTTP授权请求头参数和URI查询参数。JWT会把Claims转换成JSON格式,而这个JSON内容将会应用为JWS结构的有效载荷或者应用为JWE结构的(加密处理后的)原始字符串,通过消息认证码(Message Authentication Code或者简称MAC)和/或者加密操作对Claims进行数字签名或者完整性保护。
这里有三个概念在其他规范文件中,简单提一下:
JWE(规范文件RFC 7516):JSON Web Encryption,表示基于JSON数据结构的加密内容,加密机制对任意八位字节序列进行加密、提供完整性保护和提高破解难度,JWE中的紧凑序列化布局如下
BASE64URL(UTF8(JWE Protected Header)) || '.' || BASE64URL(JWE Encrypted Key) || '.' || BASE64URL(JWE Initialization Vector) || '.' || BASE64URL(JWE Ciphertext) || '.' || BASE64URL(JWE Authentication Tag)JWS(规范文件RFC 7515):JSON Web Signature,表示使用JSON数据结构和BASE64URL编码表示经过数字签名或消息认证码(MAC)认证的内容,数字签名或者MAC能够提供完整性保护,JWS中的紧凑序列化布局如下:
ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.' || BASE64URL(JWS Payload)) || '.' || BASE64URL(JWS Signature)JWA(规范文件RFC 7518):JSON Web Algorithm,JSON Web算法,数字签名或者MAC算法,应用于JWS的可用算法列表如下:
总的来说,JWT其实有两种实现,基于JWE实现的依赖于加解密算法、BASE64URL编码和身份认证等手段提高传输的Claims的被破解难度,而基于JWS的实现使用了BASE64URL编码和数字签名的方式对传输的Claims提供了完整性保护,也就是仅仅保证传输的Claims内容不被篡改,但是会暴露明文。目前主流的JWT框架中大部分都没有实现JWE,所以下文主要通过JWS的实现方式进行深入探讨。
JWT中的ClaimsClaim有索赔、声称、要求或者权利要求的含义,但是笔者觉得任一个翻译都不怎么合乎语义,这里保留Claim关键字直接作为命名。JWT的核心作用就是保护Claims的完整性(或者数据加密),保证JWT传输的过程中Claims不被篡改(或者不被破解)。Claims在JWT原始内容中是一个JSON格式的字符串,其中单个Claim是K-V结构,作为JsonNode中的一个field-value,这里列出常用的规范中预定义好的Claim:
简称 全称 含义iss Issuer 发行方
sub Subject 主体
aud Audience (接收)目标方
exp Expiration Time 过期时间
nbf Not Before 早于该定义的时间的JWT不能被接受处理
iat Issued At JWT发行时的时间戳
jti JWT ID JWT的唯一标识
这些预定义的Claim并不要求强制使用,何时选用何种Claim完全由使用者决定,而为了使JWT更加紧凑,这些Claim都使用了简短的命名方式去定义。在不和内建的Claim冲突的前提下,使用者可以自定义新的公共Claim,如:
简称 全称 含义cid Customer ID 客户ID
rid Role ID 角色ID
一定要注意,在JWS实现中,Claims会作为payload部分进行BASE64编码,明文会直接暴露,敏感信息一般不应该设计为一个自定义Claim。
JWT中的Header在JWT规范文件中称这些Header为JOSE Header,JOSE的全称为Javascript Object Signature Encryption,也就是Javascript对象签名和加密框架,JOSE Header其实就是Javascript对象签名和加密的头部参数。下面列举一下JWS中常用的Header:
简称 全称 含义alg Algorithm 用于保护JWS的加解密算法
jku JWK Set URL 一组JSON编码的公共密钥的URL,其中一个是用于对JWS进行数字签名的密钥
jwk JSON Web Key 用于对JWS进行数字签名的密钥相对应的公共密钥
kid Key ID 用于保护JWS进的密钥
x5u X.509 URL X.509相关
x5c X.509 Certificate Chain X.509相关
x5t X.509 Certificate SHA-1 Thumbprin X.509相关
x5t#S256 X.509 Certificate SHA-256 Thumbprint X.509相关
typ Type 类型,例如JWT、JWS或者JWE等等
cty Content Type 内容类型,决定payload部分的MediaType
最常见的两个Header就是alg和typ,例如:
{ "alg": "HS256", "typ": "JWT" } JWT的布局