JWT是JSON Web Token的缩写,即JSON Web令牌,是一种自包含令牌。
JWT的使用场景:
-
一种情况是webapi,类似之前的阿里云播放凭证的功能
-
另一种情况是多web服务器下实现无状态分布式身份验证
JWT的作用:
-
JWT 最重要的作用就是对 token信息的防伪作用
JWT的原理:
-
一个JWT由三个部分组成:JWT头、有效载荷、签名哈希最后由这三者组合进行base64编码得到
-
该对象为一个很长的字符串,字符之间通过"."分隔符分为三个子串。
-
每一个子串表示了一个功能块,总共有以下三个部分:JWT头、有效载荷和签名
JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。
{
"alg": "HS256",
"typ": "JWT"
}
在上面的代码中,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。
有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择。
sub: 主题
iss: jwt签发者
aud: 接收jwt的一方
iat: jwt的签发时间
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
以上默认字段外,我们还可以自定义私有字段,如下例
{
"name": "Helen",
"admin": true,
"avatar": "helen.jpg"
}
请注意,默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。
JSON对象也使用Base64 URL算法转换为字符串保存。
签名哈希
签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。
首先,需要指定一个密码(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(claims), secret) ==> 签名hash
使用
1.引入pom
<dependencies>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2.创建JWT、和解析JWT
public class JwtTests {
//过期时间,毫秒,24小时
private static long tokenExpiration = 24*60*60*1000;
//秘钥
private static String tokenSignKey = "atguigu123";
//创建JWT
@Test
public void testCreateToken(){
//JWT 由三部分组合 头 载荷 签名哈希
String token = Jwts.builder()
//头
.setHeaderParam("typ", "JWT") //令牌类型
.setHeaderParam("alg", "HS256") //签名算法
//载荷 :默认信息
.setSubject("guli-user") //令牌主题
.setIssuer("atguigu")//签发者
.setAudience("atguigu")//接收者
.setIssuedAt(new Date())//签发时间
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration)) //过期时间
.setNotBefore(new Date(System.currentTimeMillis() + 20*1000)) //20秒后可用
.setId(UUID.randomUUID().toString())//jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
//载荷 :自定义信息
.claim("nickname", "Helen")
.claim("avatar", "1.jpg")
//签名哈希
.signWith(SignatureAlgorithm.HS256, tokenSignKey)//签名哈希
//组装jwt字符串
.compact(); //转换成字符串
System.out.println(token);
}
解析JWT
@Test
public void testGetUserInfo(){
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJndWxpLXVzZXIiLCJpc3MiOiJhdGd1aWd1IiwiYXVkIjoiYXRndWlndSIsImlhdCI6MTY5MTIyMDc5NywiZXhwIjoxNjkxMzA3MTk3LCJuYmYiOjE2OTEyMjA4MTcsImp0aSI6Ijk1ZmEyNzBhLTg2NTQtNDRiNy1iMzkzLTYzYWJiYmE1ODBhMSIsIm5pY2tuYW1lIjoiSGVsZW4iLCJhdmF0YXIiOiIxLmpwZyJ9.4AMALjh5zrQCt4l09uM4g3b3PkUwqhUSVWHeEjLMNt0";
Jws<Claims> claimsJws = Jwts.parser()//生成解析器
.setSigningKey(tokenSignKey)//使用密钥解析
.parseClaimsJws(token);//放入token
Claims claims = claimsJws.getBody();//有校载荷部分
//取出载荷默认部分
String subject = claims.getSubject();
String issuer = claims.getIssuer();
String audience = claims.getAudience();
Date issuedAt = claims.getIssuedAt();
Date expiration = claims.getExpiration();
Date notBefore = claims.getNotBefore();
String id = claims.getId();
System.out.println(subject);
System.out.println(issuer);
System.out.println(audience);
System.out.println(issuedAt);
System.out.println(expiration);
System.out.println(notBefore);
System.out.println(id);;
String nickname = (String)claims.get("nickname");//取出载荷自定义部分
String avatar = (String)claims.get("avatar");//取出载荷自定义部分
System.out.println(nickname);
System.out.println(avatar);
}
}