一、概述
JSON Web Token (JWT) 是一个开放标准 (
RFC 7519
),它定义了一种紧凑且自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。该信息可以被验证和信任,因为它是经过数字签名的。JWT 可以使用秘密(使用
HMAC
算法)或使用
RSA
或
ECDSA
的公钥/私钥对进行
签名
。
虽然 JWT 可以加密以在各方之间提供保密,但我们将重点关注
签名
令牌。签名令牌可以验证其中包含的声明的
完整性
,而加密令牌则对其他方
隐藏
这些声明。当使用公钥/私钥对对令牌进行签名时,签名还证明只有持有私钥的一方才是对其进行签名的一方。
二、使用场景
授权
:
这是使用 JWT 最常见的场景。用户登录后,每个后续请求都将包含 JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用 JWT 的一项功能,因为它的开销很小,并且能够轻松地跨不同域使用。
信息交换
:
JSON Web Tokens 是一种在各方之间安全传输信息的好方法。因为 JWT 可以被签名——例如,使用公钥/私钥对——你可以确定发件人就是他们所说的那样。此外,由于使用标头和有效负载计算签名,因此您还可以验证内容是否未被篡改。
三、JWT结构
在其紧凑形式中,JSON Web Tokens 由用点 (
.
)分隔的三个部分组成,它们是:
标题
默认情况下标头
通常
由两部分组成:令牌的类型,即 JWT,以及正在使用的签名算法,例如 HMAC SHA256 或 RSA,也可以在默认的类型和签名算法添加新的信息。
默认生成的头部信息,alg根据使用的签名算法自动生成。
{
“alg”: “HS256”,
“typ”: “JWT”
}
可在默认头部信息外添加新的信息
//默认之外添加的新的头部信息 Map<String, Object> stringStringHashMap = new HashMap<>(); stringStringHashMap.put("type","jwt"); stringStringHashMap.put("name","test"); stringStringHashMap.put("time", TimestampUtils.getTimestamp(new Date())); //测试负载部分的数组 Long[] id = new Long[]{1L,2L,3L}; //生成jwt负载信息 String sign = JWT.create() //头部信息 .withHeader(stringStringHashMap) //有效载荷 .withClaim("t1", TimestampUtils.getTimestamp(new Date())) .withClaim("t2", TimestampUtils.getTimestamp(new Date())) .withClaim("t3", TimestampUtils.getTimestamp(new Date())) .withClaim("t4", TimestampUtils.getTimestamp(new Date())) .withClaim("t5", TimestampUtils.getTimestamp(new Date())) .withClaim("t6", TimestampUtils.getTimestamp(new Date())) .withArrayClaim("id", id) //签名 .sign(hhhh);
有效载荷
令牌的第二部分是负载,说白了就是我们要放用户的一些非敏感信息。
//默认之外添加的新的头部信息 Map<String, Object> stringStringHashMap = new HashMap<>(); stringStringHashMap.put("type","jwt"); stringStringHashMap.put("name","test"); stringStringHashMap.put("time", TimestampUtils.getTimestamp(new Date())); //测试负载部分的数组 Long[] id = new Long[]{1L,2L,3L}; //生成jwt负载信息 String sign = JWT.create() //头部信息 .withHeader(stringStringHashMap) //有效载荷 .withClaim("t1", TimestampUtils.getTimestamp(new Date())) .withClaim("t2", TimestampUtils.getTimestamp(new Date())) .withClaim("t3", TimestampUtils.getTimestamp(new Date())) .withClaim("t4", TimestampUtils.getTimestamp(new Date())) .withClaim("t5", TimestampUtils.getTimestamp(new Date())) .withClaim("t6", TimestampUtils.getTimestamp(new Date())) .withArrayClaim("id", id) //签名 .sign(hhhh);
签名
要创建签名部分,必须获取jwt的头部、有效载荷、密钥加上指定的算法,并对其进行签名。
因此,JWT 通常如下所示。
xxxxx.yyyyy.zzzzz
eyJuYW1lIjoidGVzdCIsInR5cCI6IkpXVCIsInRpbWUiOjE2Mjg0MjMxOTUzOTUsInR5cGUiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6WzEsMiwzXX0.s5_2UOEvNvd-n3mjB8fIzW0sqwlvLG9xqTGVbF5651M
四、实例演示
jwt的依赖
<!--jwt--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency>
代码
package com.test.Utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.codec.binary.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class JwtUtils {
private static final String pwd = “123456”;
//生成jwtToken
public static String CreateToken(){
//签名使用的密钥
Algorithm hhhh = Algorithm.HMAC256(pwd);
//生成jwt头部信息
/**
* 如果不添加头部信息,默认会把添加
* {
* “typ”: “JWT”,
* “alg”: “加密类型”
* }
*/
Map<String, Object> stringStringHashMap = new HashMap<>();
stringStringHashMap.put(“type”,”jwt”);
stringStringHashMap.put(“name”,”test”);
stringStringHashMap.put(“time”,
TimestampUtils.getTimestamp(new Date()));
//测试负载部分的数组
Long[] id = new Long[]{1L,2L,3L};
//生成jwt负载信息
String sign = JWT.create()
.withHeader(stringStringHashMap)
.withClaim(“t1”, TimestampUtils.getTimestamp(new Date()))
.withClaim(“t2”, TimestampUtils.getTimestamp(new Date()))
.withClaim(“t3”, TimestampUtils.getTimestamp(new Date()))
.withClaim(“t4”, TimestampUtils.getTimestamp(new Date()))
.withClaim(“t5”, TimestampUtils.getTimestamp(new Date()))
.withClaim(“t6”, TimestampUtils.getTimestamp(new Date()))
.withArrayClaim(“id”, id)
.sign(hhhh);
return sign;
}
//验证jwt
public static void verify(String jwtStr){
JWTVerifier build = JWT.require(Algorithm.HMAC256(pwd)).build();
try{
//验签
DecodedJWT verify = build.verify(jwtStr);
System.out.println(“完整的token”);
System.out.println(verify.getToken());
System.out.println(“头部信息”);
System.out.println(verify.getHeader());
System.out.println(“负荷信息”);
System.out.println(verify.getPayload());
System.out.println(“签名信息”);
System.out.println(verify.getSignature());
//获取主体对应信息
System.out.println(“获取主体对应信息”);
byte[] bytes = Base64.decodeBase64(verify.getPayload());
System.out.println(new String(bytes));
System.out.println(“获取头部信息”);
byte[] bytes1 = Base64.decodeBase64(verify.getHeader());
System.out.println(new String(bytes1));
System.out.println(“获取签名信息”);
byte[] bytes2 = Base64.decodeBase64(verify.getSignature());
System.out.println(new String(bytes2));
//获取所有负荷信息中的所有Claims
System.out.println(“获取所有负荷信息中的所有Claims”);
Map<String, Claim> claims = verify.getClaims();
//
Iterator<Map.Entry<String, Claim>> iterator = claims.entrySet().iterator();
//遍历
while (iterator.hasNext()) {
Map.Entry<String, Claim> next = iterator.next();
System.out.println(next.getKey()+”:”+next.getValue().asLong());
}
//获取头部信息
System.out.println(verify.getHeaderClaim(“typ”).asString());
}
catch (AlgorithmMismatchException e){
//算法错误
System.out.println(“加密算法和解密算法不一致”);
}
catch (SignatureVerificationException e){
//验签失败,验签pwd密码错误
System.out.println(“解密密码错误”);
}
catch (TokenExpiredException e){
//token过期
System.out.println(“Token过期”);
}
catch (InvalidClaimException e){
//获取不到对应的负荷信息
System.out.println(“获取不到对应的负荷信息”);
}
}
public static void main(String[] args) {
String token = CreateToken();
System.out.println(token);
verify(token);
}
}
五、jwt官网地址