JWT
1,JWT介绍
全名:Json W eb Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名。
JWT一般放入是做头部一起进行请求Authorization ,所以跨域资源共(CORS)不会成为问题,因为它不使用cookie。
2,JWT组成结构
一个JWT实际上就是一个字符串,由三部分组成头部,载荷,签名
通常情况下
xxxxx.yyyyy.zzzzz
1,头部(Header)
头部通常由两部分组成:令牌的类型(即JWT)和所使用的签名加密算法,例如HMAC SHA256或RSA。
{
"alg": "HS256",
"typ": "JWT"
}
然后此json被base64编码,形成JWT的第一部分(可解码)
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
2,载荷(payload)
载荷就是存放有效信息的地方,这些有效信息包括三部分
1,Registered claims: 注册声明
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
2,Public claims: 公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
3,Private claims:私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息,这个指的就是自定义的claim
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
像admin:true就是自定义的claim,根据自己的验证规则,确定是否有用
然后进行base64编码(可解密),得到JWT的第二部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
3,签名(signature)
要创建签名部分,您必须获取编码的Header,编码的payload,secret,然后用Header中指定的算法,三部分组成。
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐 secret(一般是放在服务端,可以理解为密钥) 组合加密,然后就构成了jwt的第三部分
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
形成
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
将这三部分用.连接成一个完整的字符串,构成了最终的JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6I
kpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
3,Java实现JWT(JJWT)
JJWT是一个提供端到端的JWT创建和验证的Java库。永远免费和开源(Apache
License,版本2.0),JJWT很容易使用和理解。它被设计成一个以建筑为中心的流畅界面,隐藏了它的大部分复杂性。
3.1 引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
3.2 生成不过期的token
1,创建类CreateJwtDemo.java
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
/**
* @Author : FuYu
* @Despriotion : 生成不过期token
*/
public class CreateJwtDemo {
public static void main(String[] args) {
JwtBuilder builder= Jwts.builder().setId("123")
.setSubject("小白")
//签发时间
.setIssuedAt(new Date())
//签名密钥 (jjwt)加密
.signWith(SignatureAlgorithm.HS256,"jjwt");
System.out.println( builder.compact() );
}
}
控制台
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjIxODB9.txnBHKh7Hf0in0vNwPj8fwho4Ze31Z5lJ1GqUoV5ScM
再次运行,会发现每次运行的结果是不一样的,因为我们的载荷中包含了时间。
3.3 解析不过期的token
1,创建类ParseJwtDemo.java
/**
* @Author : FuYu
* @Despriotion : 解析不过期token
*/
public class ParseJwtDemo {
public static void main(String[] args) {
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjIxODB9.txnBHKh7Hf0in0vNwPj8fwho4Ze31Z5lJ1GqUoV5ScM";
Claims claims = Jwts.parser().setSigningKey("jjwt").parseClaimsJws(token).getBody();
System.out.println("id:" + claims.getId());
System.out.println("subject:" + claims.getSubject());
System.out.println("IssuedAt:" + claims.getIssuedAt());
}
}
前提:要知道密钥,先前密钥是jjwt,所以这里用jjwt做密钥解密
控制台
id:123
subject:小白
IssuedAt:Fri Oct 04 12:09:40 CST 2019
试试把jjwt换成别的密钥,会发现报错
3.4 生成可过期token的
有很多时候,我们并不希望签发的token是永久生效的,所以我们可以为token添加一个过期时间
1,创建CreateJwtDemo2
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
/**
* @Author : FuYu
* @Despriotion : 生成会过期的的token
*/
public class CreateJwtDemo2 {
public static void main(String[] args) {
//当前时间
long now = System.currentTimeMillis();
//过期时间为1分钟
long exp = now + 1000 * 60;
JwtBuilder builder = Jwts.builder().setId("123")
.setSubject("小白")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "jjwt")
//设置过期时间
.setExpiration(new Date(exp));
System.out.println(builder.compact());
}
}
控制台
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjI4OTksImV4cCI6MTU3MDE2Mjk1OX0.pqNSTcAMMiG2kSUFJTIQcu7GEZ9xaCTd18aW3cbQhXM
3.5 解析可过期的token
1,创建ParseJwtDemo2.java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author : FuYu
* @Despriotion : 解析会过期token
*/
public class ParseJwtDemo2 {
public static void main(String[] args) {
String compactJws = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjI4OTksImV4cCI6MTU3MDE2Mjk1OX0.pqNSTcAMMiG2kSUFJTIQcu7GEZ9xaCTd18aW3cbQhXM";
Claims claims = Jwts.parser().setSigningKey("jjwt").parseClaimsJws(compactJws).getBody();
System.out.println("id:" + claims.getId());
System.out.println("subject:" + claims.getSubject());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss");
System.out.println("签发时间:" + sdf.format(claims.getIssuedAt()));
System.out.println("过期时 间:"+sdf.format(claims.getExpiration()));
System.out.println("当前时间:" + sdf.format(new Date()));
}
}
控制台
id:123
subject:小白
签发时间:2019‐10‐04 12:21:39
过期时 间:2019‐10‐04 12:22:39
当前时间:2019‐10‐04 12:22:18
但是等一分钟后再运行,会发现控制台报错,jwt过期报错
Exception in thread "main" io.jsonwebtoken.ExpiredJwtException: JWT expired at 2019-10-04T12:22:39Z. Current time: 2019-10-04T12:23:01Z, a difference of 22250 milliseconds. Allowed clock skew: 0 milliseconds.
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:385)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)
at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541)
at com.fy.ParseJwtDemo2.main(ParseJwDemo2.java:16)
3.6 生成自定义claims的token
1,创建CreateJwtDemo3.java
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
/**
* @Author : FuYu
* @Despriotion : 自定义claims
*/
public class CreateJwtDemo3 {
public static void main(String[] args) {
//当前时间
long now = System.currentTimeMillis();
//过期时间为1分钟
long exp = now + 1000 * 60;
JwtBuilder builder = Jwts.builder().setId("123")
.setSubject("小白")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "jjwt")
.setExpiration(new Date(exp))
//自定义claim
.claim("roles", "admin")
.claim("logo", "logo.png");
System.out.println(builder.compact());
}
}
控制台
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjMyMDgsImV4cCI6MTU3MDE2MzI2OCwicm9sZXMiOiJhZG1pbiIsImxvZ28iOiJsb2dvLnBuZyJ9._8YdsqBQSW1ctNmoXVTVpGWyYm8wsF-6Pt2ZKbXfyrk
3.7 解析自定义claims的token
1,创建ParseJwtDemo3.java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author : FuYu
* @Despriotion : 解析自定义的claims token
*/
public class ParseJwtDemo3 {
public static void main(String[] args) {
String compactJws="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjMyMDgsImV4cCI6MTU3MDE2MzI2OCwicm9sZXMiOiJhZG1pbiIsImxvZ28iOiJsb2dvLnBuZyJ9._8YdsqBQSW1ctNmoXVTVpGWyYm8wsF-6Pt2ZKbXfyrk";
Claims claims = Jwts.parser().setSigningKey("jjwt").parseClaimsJws(compactJws).getBody();
System.out.println("id:"+claims.getId());
System.out.println("subject:"+claims.getSubject());
System.out.println("roles:"+claims.get("roles"));
System.out.println("logo:"+claims.get("logo"));
SimpleDateFormat sdf=new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss");
System.out.println("签发时间:"+sdf.format(claims.getIssuedAt()));
System.out.println("过期时间:"+sdf.format(claims.getExpiration()));
System.out.println("当前时间:"+sdf.format(new Date()) );
}
}
控制台
id:123
subject:小白
roles:admin
logo:logo.png
签发时间:2019‐10‐04 12:26:48
过期时间:2019‐10‐04 12:27:48
当前时间:2019‐10‐04 12:27:47
注意:如果claim中没有要取得值,会返回null,而不是报错
JWT工具类
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
/**
* jwt工具类
*
* @author FuYu
*/
public class JwtUtil {
//密钥
private String key;
/**
* 一个小时
*/
private long ttl;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
/**
* 生成JWT
*
* @param id
* @param subject
* @return
*/
public String createJWT(String id, String subject, String roles) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
JwtBuilder builder = Jwts.builder().setId(id)
.setSubject(subject)
.setIssuedAt(now)
.signWith(SignatureAlgorithm.HS256, key);
if (ttl > 0) {
builder.setExpiration(new Date(nowMillis + ttl));
}
return builder.compact();
}
/**
* 解析JWT
*
* @param jwtStr
* @return
*/
public Claims parseJWT(String jwtStr) {
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(jwtStr)
.getBody();
}
}
总结:jwt用了很长一段时间得,但是一直没有写笔记,总觉得很简单,事实上也确实是很简单。
网上找资料,自己写demo,会有进步的