本文最后更新于:May 13, 2023 pm
JSON Web 令牌是一种开放的行业标准 RFC 7519方法,用于在两方之间安全地表示声明。JSON Web Token (JWT) 是一个开放标准 ( RFC 7519 ),它定义了一种紧凑且自包含的方式,用于在各方之间以 JSON 对象的形式安全传输信息。此信息可以验证和信任,因为它是数字签名的。JWT 可以使用密钥(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。官网
目录
JWT结构
令牌组成
- 标头(Header)
- 有效载荷(Payload)
- 签名(Signature)
JWT通常形式为:xxxx.yyyy.zzzz,对应Header.Payload.Signature。
注意:Base64是一种编码,而不是加密。Base64编码是可以被还原的。(可逆的)
标头由两部分组成:令牌的类型(即JWT)和所使用的签名算法。例如:HMACSHA256(HS256) 或RSA。还会进行Base64编码,进而组成JWT的第一部分。
| { "alg": "HS256", "typ": "JWT" }
|
上述是简写了的:alg(Algorithm)、typ(type)。
Payload
有效负载,其中包含声明。声明有关实体(通常为用户)和其他数据的声明(简单说就是用户信息,但不要放用户的敏感信息!)。也是进行Base64编码,进而组成JWT的第二部分。
| { "id": "123456", "name": "loong", "role": "admin" }
|
Signature
前两者(Header、Payload)用Base64编码后,再进行Header中指定的算法(本例中是 HS256)进行签名,最后组成Signature(不要忘记点号(.
))。最后组成JWT的第三部分。
| HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret);
|
参考
如果一个令牌为:xxx.yyy.zzz,那么,
所以,如果 T = xxx+yyy ,那么:
- T = base64UrlEncode(header).base64UrlEncode(payload)
如果,zzz = T + 签名,那么:
zzz = T + 签名;
zzz = HMACSHA256(T,secret);
zzz = HMACSHA256(base64UrlEncode(header)+”.”+base64UrlEncode(payload),secret)
所以,最后的结果为:
| base64UrlEncode(header). base64UrlEncode(payload). HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
|
生成Token
导入依赖
这里以java-jwt
为例:
| <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.18.3</version> </dependency>
|
| payload官方定义包含属性如下(非强制): iss: jwt签发者 sub: jwt所面向的用户 aud: 接收jwt的一方 exp: jwt的过期时间,这个过期时间必须要大于签发时间 nbf: 定义在什么时间之前,该jwt都是不可用的. iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Test void contextLoads() { Algorithm alg = Algorithm.HMAC256("sdfwefe"); Map<String,Object> header = new HashMap<>(); Calendar ins = Calendar.getInstance(); ins.add(Calendar.SECOND,60);
String token = JWT.create() .withHeader(header) .withIssuedAt(new Date()) .withClaim("userid",23) .withClaim("username","loong") .withExpiresAt(ins.getTime()) .sign(alg);
System.out.println(token); }
|
放置的位置有一定的限制!签名放最后,不能在签名后再加其他的。
验证Token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @Test void contextLoads2() { Algorithm alg = Algorithm.HMAC256("sdfwefe"); JWTVerifier jwtVerifier = JWT.require(alg).build(); String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NDM5NDk0MzAsImlhdCI6MTY0Mzk0OTM3MCwidXNlcmlkIjoyMywidXNlcm5hbWUiOiJsb29uZyJ9.s2rvfcW5wSvEtCDP0189Sxh7LItbWZKMG2xZ4-Rb220"; DecodedJWT jwt = jwtVerifier.verify(token);
String decodeHeader = StringUtils.newStringUtf8(Base64.decodeBase64(jwt.getHeader())); String decodePayload = StringUtils.newStringUtf8(Base64.decodeBase64(jwt.getPayload())); String signature = jwt.getSignature();
int userid = jwt.getClaim("userid").asInt(); String username = jwt.getClaim("username").asString();
System.out.println("header:" + jwt.getHeader()); System.out.println("payload:" + jwt.getPayload()); System.out.println("signature:" + signature);
System.out.println("headerString:" + decodeHeader); System.out.println("payloadString:" + decodePayload);
System.out.println("nameid:" + userid); System.out.println("username:" + username);
System.out.println("过期时间为: "+ jwt.getExpiresAt()); }
|
输出:
| header:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 payload:eyJleHAiOjE2NDM5NDk0MzAsImlhdCI6MTY0Mzk0OTM3MCwidXNlcmlkIjoyMywidXNlcm5hbWUiOiJsb29uZyJ9 signature:s2rvfcW5wSvEtCDP0189Sxh7LItbWZKMG2xZ4-Rb220 headerString:{"typ":"JWT","alg":"HS256"} payloadString:{"exp":1643949430,"iat":1643949370,"userid":23,"username":"loong"} nameid:23 username:loong 过期时间为: Fri Feb 04 12:37:10 CST 2022
|
封装工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| package com.tothefor.utils;
import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar; import java.util.Map;
public class JWTUtils { private static final String SIG = "123432@#@$#^$^%#^%#89";
public static String getToken(Map<String,String> map){ Algorithm alg = Algorithm.HMAC256(SIG); Calendar ins = Calendar.getInstance(); ins.add(Calendar.DATE,7); JWTCreator.Builder builder = JWT.create(); map.forEach((k,v)->{ builder.withClaim(k,v); }); String token = builder.withExpiresAt(ins.getTime()).sign(alg);
return token; }
public static DecodedJWT getTokenInfo(String token){ DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIG)).build().verify(token); return verify; } }
|