1、什么是JWT?
JSON Web Token (JWT)是一个开放的行业标准(RFC 7519),它定义了一种简洁的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公钥/私钥对进行签名,防止被篡改。
JWT令牌的优点:
JWT基于json,非常方便解析。
可以在令牌中自定义丰富的内容,易扩展。
通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。
资源服务使用JWT可不依赖认证服务即完成授权。
JWT令牌的缺点:
JWT令牌较长,占存储空间比较大
2、JWT组成
一个JWT实际上就一个字符串,它由三部分组成,头部、负载与签名。
1、头部(Header)
头部用于描述关于该JWT的最基本信息,例如其类型(即JWT)以及签名所用的算法(如HMAC SHA256 或 RSA)等。这也可以被表示成一个JSON对象。
JSON字符串
{
"alg":"HS256",
"typ":"JWT"
}
alg:签名算法
typ:类型
我们对头部的json字符串进行BASE64编码,编码后的字符串如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Base64是一种基于64个可打印字符串来表示二进制数据的表示方式。JDK提供了非常方便的Base64Encoder和Base64Decoder,用它们可以非常方便的完成基于Base64的编码和解码。
2、负载(Payload)
负载,是存放有效信息的地方,比如用户的基本信息可以存在该部分中。负载包含三个部分:
标准中注册的声明(建议但不强制使用)
iss:jwt签发者
sub:jwt所面向的用户
aud:接收jwt的一方
exp:jwt的过期时间,过期时间必须大于签发时间
nbf:定义在什么时间之前,该jwt都是不可用的
iat:jwt的签发时间
jti:jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共的声明
公共的声明可以添加任何信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密。
私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
私有声明也就是自定义claim,用于存放自定义键值对
Json字符串
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
其中sub是标准的声明,name是自定义的私有声明,编码后如下:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
3、签证、签名(Signature)
jwt的第三部分是一个签证信息,由三部分组成:
Header(Base64编码后)
Payload(Base64编码后)
Secret(盐,必须保密)
这个部分需要Base64加密后的header和base4加密后的payload使用.连接组成的字符串,然后通过header重声明的加密方式进行加盐Secret组合加密,然后就构成了JWT的第三部分——使用“qfjava”作为盐:
eZqdTo1mRMB-o7co1oAiTvNvumfCkt-1H-CdfNm78Cw
三个部分组合出的完整字符串:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.eZqdTo1mRMB-o7co1oAiTvNvumfCkt-1H-CdfNm78Cw
注意:secret是保存在服务器端的,jwt在签发生成也是在服务器端的,secret就是用来进行jwt的签发和验证,所以,它就是服务器端的私钥,在任何场景都不应该泄漏。一旦客户端得知这个secret,那就意味着客户端是可以自我签发jwt了。
3、Token与·JWT
3.1 token与JWT
token是一个很宽泛的概念,翻译为令牌,一般用来表示经过验证之后得到的凭证,长度没有什么限制,多长都可以。
jwt是 JSON Web Token,它也自称是一种token,jwt就是一个很具体的标准了,用点号分为三段,分别表示头、信息和签名。token有很多种,可以是标准的,也可以是你自己定义的,jwt则是其中一种token,而且是标准的token。和我们自己随意定义的token差别大是很自然的,因为我们自己定义的token只需要用来识别用户登录状态,一般很短的uuid都可以实现,所以比较短。
4.使用JJWT
JJWT是一个提供端到端的JWT创建和验证的开源Java库。也就是说使用JJWT能快速完成JWT的功能开发。
4.1、引入依赖
创建Springboot工程并引入jjwt依赖,pom.xml如下:
<!--jjwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>RELEASE</version>
</dependency>
4.2、创建Token
//定义token时效和签名
private long expiration = 60 * 60 * 24 * 1000;
private String signature = "admin";
@Test
public void t1() {
JwtBuilder jwtBuilder = Jwts.builder();
String jwtToken = jwtBuilder
//header
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
//payload
.claim("username", "cinco")//自定义载荷
.claim("role", "admin")//自定义载荷
.setSubject("admin-test")
.setExpiration(new Date(System.currentTimeMillis() + expiration))//过期时间
.setId("1001")
//signature
.signWith(SignatureAlgorithm.HS256, signature)
.compact();
System.out.println(jwtToken);
}
4.3、JWT将用户信息转换成Token字符串,生成结果如下:
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMDAxIiwic3ViIjoi5bCP5piOIiwiaWF0IjoxNjE1MzY2MDEyfQ.2LNcw1v64TNQ96eCpWKvtAccBUA-cEVMDyJNMef-zu0
4.4、解析Token
通过JWT解析Token,获取Token中存放的用户信息,即生成Claims对象。
@Test
public void testParseToken() {
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImNpbmNvIiwicm9sZSI6ImFkbWluIiwic3ViIjoiYWRtaW4tdGVzdCIsImV4cCI6MTY4NjIyMDM4NiwianRpIjoiMTAwMSJ9.TfWVd8cMZx3CP09WY35LagV6s6HS2WKXpH83iFJrzQE";
//解析Token,生成Claims对象,Token中存放的用户信息解析到了claims对象中
Claims claims = Jwts.parser()
.setSigningKey(signature)
.parseClaimsJws(token)
.getBody();
System.out.println(claims);
System.out.println(claims.get("username"));
System.out.println(claims.get("role"));
System.out.println(claims.getId());
System.out.println(claims.getSubject());
System.out.println(claims.getExpiration());
}