冷饭新炒:理解JWT的实现原理和基本使用 (2)

主要介绍JWS的布局,前面已经提到过,JWS的紧凑布局如下:

ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.' || BASE64URL(JWS Payload)) || '.' || BASE64URL(JWS Signature)

其实还有非紧凑布局,会通过一个JSON结构完整地展示Header参数、Claims和分组签名:

{ "payload":"<payload contents>", "signatures":[ {"protected":"<integrity-protected header 1 contents>", "header":<non-integrity-protected header 1 contents>, "signature":"<signature 1 contents>"}, ... {"protected":"<integrity-protected header N contents>", "header":<non-integrity-protected header N contents>, "signature":"<signature N contents>"}] }

非紧凑布局还有一个扁平化的表示形式:

{ "payload":"<payload contents>", "protected":"<integrity-protected header contents>", "header":<non-integrity-protected header contents>, "signature":"<signature contents>" }

其中Header参数部分可以参看上一小节,而签名部分可以参看下一小节,剩下简单提一下payload部分,payload(有效载荷)其实就是完整的Claims,假设Claims的JSON形式是:

{ "iss": "throwx", "jid": 1 }

那么扁平化非紧凑格式下的payload节点就是:

{ ...... "payload": { "iss": "throwx", "jid": 1 } ...... } JWS签名算法

JWS签名生成依赖于散列或者加解密算法,可以使用的算法见前面贴出的图,例如HS256,具体是HMAC SHA-256,也就是通过散列算法SHA-256对于编码后的Header和Claims字符串进行一次散列计算,签名生成的伪代码如下:

## 不进行编码 HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), 256 bit secret key ) ## 进行编码 base64UrlEncode( HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload) [256 bit secret key]) )

其他算法的操作基本相似,生成好的签名直接加上一个前置的.拼接在base64UrlEncode(header).base64UrlEncode(payload)之后就生成完整的JWS。

JWT的生成、解析和校验

前面已经分析过JWT的一些基本概念、布局和签名算法,这里根据前面的理论进行JWT的生成、解析和校验操作。先引入common-codec库简化一些编码和加解密操作,引入一个主流的JSON框架做序列化和反序列化:

<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.15</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.0</version> </dependency>

为了简单起见,Header参数写死为:

{ "alg": "HS256", "typ": "JWT" }

使用的签名算法是HMAC SHA-256,输入的加密密钥长度必须为256 bit(如果单纯用英文和数字组成的字符,要32个字符),这里为了简单起见,用00000000111111112222222233333333作为KEY。定义Claims部分如下:

{ "iss": "throwx", "jid": 10087, # <---- 这里有个笔误,本来打算写成jti,后来发现写错了,不打算改 "exp": 1613227468168 # 20210213 }

生成JWT的代码如下:

@Slf4j public class JsonWebToken { private static final String KEY = "00000000111111112222222233333333"; private static final String DOT = "."; private static final Map<String, String> HEADERS = new HashMap<>(8); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); static { HEADERS.put("alg", "HS256"); HEADERS.put("typ", "JWT"); } String generateHeaderPart() throws JsonProcessingException { byte[] headerBytes = OBJECT_MAPPER.writeValueAsBytes(HEADERS); String headerPart = new String(Base64.encodeBase64(headerBytes,false ,true), StandardCharsets.US_ASCII); log.info("生成的Header部分为:{}", headerPart); return headerPart; } String generatePayloadPart(Map<String, Object> claims) throws JsonProcessingException { byte[] payloadBytes = OBJECT_MAPPER.writeValueAsBytes(claims); String payloadPart = new String(Base64.encodeBase64(payloadBytes,false ,true), StandardCharsets.UTF_8); log.info("生成的Payload部分为:{}", payloadPart); return payloadPart; } String generateSignaturePart(String headerPart, String payloadPart) { String content = headerPart + DOT + payloadPart; Mac mac = HmacUtils.getInitializedMac(HmacAlgorithms.HMAC_SHA_256, KEY.getBytes(StandardCharsets.UTF_8)); byte[] output = mac.doFinal(content.getBytes(StandardCharsets.UTF_8)); String signaturePart = new String(Base64.encodeBase64(output, false ,true), StandardCharsets.UTF_8); log.info("生成的Signature部分为:{}", signaturePart); return signaturePart; } public String generate(Map<String, Object> claims) throws Exception { String headerPart = generateHeaderPart(); String payloadPart = generatePayloadPart(claims); String signaturePart = generateSignaturePart(headerPart, payloadPart); String jws = headerPart + DOT + payloadPart + DOT + signaturePart; log.info("生成的JWT为:{}", jws); return jws; } public static void main(String[] args) throws Exception { Map<String, Object> claims = new HashMap<>(8); claims.put("iss", "throwx"); claims.put("jid", 10087L); claims.put("exp", 1613227468168L); JsonWebToken jsonWebToken = new JsonWebToken(); System.out.println("自行生成的JWT:" + jsonWebToken.generate(claims)); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjzjp.html