简介
什么是JWT?
JSON Web Token,通过数字签名的方式,以JSON对象为载体,在不同的服务终端之间安全的传输信息
JWT有什么用?
JWT最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含JWT,系统在每次处理用户请求的之前,都要先进行JWT安全校验,通过之后再进行处理。
JWT的组成
JWT由3部分组成,用拼接
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IjZiOTJkNiIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MjUwMjEyMDcsImp0aSI6IjNiYzcxYTM2LWUxNjItNDEwYS04YTQyLWU1YzY4YTczZTRjZSJ9.UI_Cv0GEW54e9uaz39Ag0V3HreDwxLSlaSfuVuGhZIM
三部分分别是:
Header:
- jwt的头部由两部分信息组成:
- type:声明类型,这里是jwt
- alg:声明加密的算法 通常直接使用 HMAC SHA256
- 完整的头部信息如下:
{
'typ': 'JWT',
'alg': 'HS256'
}
对头部信息进行Base64编码的得到第一部分的信息
Payload:
载荷就是存放有效信息的地方,它包含声明(要求)。声明有三种类型:
- registered claims:标准中注册的声明。这里有一组预定义的声明,它们不是强制的,但是推荐
- public claims:公共的声明
- private claims:私有的声明
标准中注册的声明 (建议但不强制使用) :
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
公共的声明 :公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有的声明 :私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
一个json字符创,包含一些自定义的信息,
{
"sub": "1234567890",
"name":"John Doe",
"admin":true
}
使用base64加密
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
Signature
由头部信息使用base64加密之后,拼接上载荷使用base64加密之后的部分,在加上当前的密钥,进行头部中的加密算法进行加密
header (base64后的)
payload (base64后的)
secret
第三部分需要base64加密后的header和base64加密后的payload使用 .
连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了JWT的第三部分。
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
var encodedstring = base64urlEncode(header)+'.'+base64UrlEncode(payload);
var signature = HMACSHA256 (encodedstring,'secret');
注意:
secret是保存在服务器端的,JWT的签发生成也是在服务器端的,secret就是用来进行JWT的签发和JWT的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发JWT了。
将这三部分用.连接成一个完整的字符串,构成了最终的jwt:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IjZiOTJkNiIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MjUwMjEyMDcsImp0aSI6IjNiYzcxYTM2LWUxNjItNDEwYS04YTQyLWU1YzY4YTczZTRjZSJ9.UI_Cv0GEW54e9uaz39Ag0V3HreDwxLSlaSfuVuGhZIM
测试
private long time = 1000 * 60 * 60 * 24;
private String signature = "admin";
/**
* 测试JWT
*/
@org.junit.Test
public void jwt() {
JwtBuilder builder = Jwts.builder();
String jwtToken = builder
// header
.setHeaderParam("typ","JWT")
.setHeaderParam("alg","HS256")
// payload
.claim("username","6b92d6")
.claim("role","admin")
// 设置主题
.setSubject("admin-test")
// 设置有效时间
.setExpiration(new Date(System.currentTimeMillis()+time))
.setId(UUID.randomUUID().toString())
// signature
.signWith(SignatureAlgorithm.HS256,signature)
// 拼接三部分
.compact();
System.out.println(jwtToken);
}
/**
* 解析
*/
@org.junit.Test
public void parse() {
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IjZiOTJkNiIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MjUwMjEyMDcsImp0aSI6IjNiYzcxYTM2LWUxNjItNDEwYS04YTQyLWU1YzY4YTczZTRjZSJ9.UI_Cv0GEW54e9uaz39Ag0V3HreDwxLSlaSfuVuGhZIM";
JwtParser parser = Jwts.parser();
Jws<Claims> claimsJws = parser.setSigningKey(signature).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
System.out.println(claims.get("username"));
System.out.println(claims.get("role"));
System.out.println(claims.getId());
System.out.println(claims.getExpiration());
}
//
/**
* 创建token
*/
@Test
public void testCreateToken() {
//创建 JwtBuilder 对象
JwtBuilder jwtBuilder = Jwts.builder()
//声明的标识,{"jti":"8888"}
.setId("8888")
//设置主体,用户{"sub":"Rose"}
.setSubject("Rose")
//创建时间{"ita":"xxxx"}
.setIssuedAt(new Date())
//创建签证,参数1:算法,参数2:盐
.signWith(SignatureAlgorithm.HS256,"xxxx");
//获取jwt生成的token
String token = jwtBuilder.compact();
System.out.println(token);
System.out.println("============");
String[] split = token.split("\\.");
System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
//无法解密签名
System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
}
/**
* 解析token
*/
@Test
public void testParseToken() {
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiUm9zZSIsImlhdCI6MTYyNjYwNjc3Nn0.pPDKl5_f5f9RHAfGqzaAiwGwQdpOUYMjiQ9BCzWlUR0";
//解析token得到负载中声明的对象
Claims claims = Jwts.parser()
//密钥
.setSigningKey("xxxx")
.parseClaimsJws(token)
//得到主体
.getBody();
System.out.println("id:" + claims.getId());
System.out.println("Subject:" + claims.getSubject());
System.out.println("IssuedAt:" + claims.getIssuedAt());
}
/**
* 创建token(失效时间)
*/
@Test
public void testCreateTokenHasExp() {
//当前时间
long now = System.currentTimeMillis();
//过期时间,一分钟
long exp = now + 60 * 1000;
//创建 JwtBuilder 对象
JwtBuilder jwtBuilder = Jwts.builder()
//声明的标识,{"jti":"8888"}
.setId("8888")
//设置主体,用户{"sub":"Rose"}
.setSubject("Rose")
//创建时间{"ita":"xxxx"}
.setIssuedAt(new Date())
//创建签证
.signWith(SignatureAlgorithm.HS256,"xxxx")
//设置过期时间
.setExpiration(new Date(exp));
//获取jwt生成的token
String token = jwtBuilder.compact();
System.out.println(token);
System.out.println("============");
String[] split = token.split("\\.");
System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
//无法解密签名
System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
}
/**
* 解析token(失效时间)
*/
@Test
public void testParseTokenHasExp() {
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiUm9zZSIsImlhdCI6MTYyNjYwNzgyOSwiZXhwIjoxNjI2NjA3ODg5fQ.6URP2EbehTS2qx_t0medQUPQxaxBWnUl0VJxuEdlboQ";
//解析token得到负载中声明的对象
Claims claims = Jwts.parser()
//密钥
.setSigningKey("xxxx")
.parseClaimsJws(token)
//得到主体
.getBody();
System.out.println("id:" + claims.getId());
System.out.println("Subject:" + claims.getSubject());
System.out.println("IssuedAt:" + claims.getIssuedAt());
//格式化时间
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("签发时间" + simpleDateFormat.format(claims.getIssuedAt()));
System.out.println("过期时间" + simpleDateFormat.format(claims.getExpiration()));
System.out.println("当前时间" + simpleDateFormat.format(new Date()));
}
/**
* 创建token(自定义申明)
*/
@Test
public void testCreateTokenByClaims() {
//创建 JwtBuilder 对象
JwtBuilder jwtBuilder = Jwts.builder()
//声明的标识,{"jti":"8888"}
.setId("8888")
//设置主体,用户{"sub":"Rose"}
.setSubject("Rose")
//创建时间{"ita":"xxxx"}
.setIssuedAt(new Date())
//创建签证,参数1:算法,参数2:盐
.signWith(SignatureAlgorithm.HS256,"xxxx")
//自定义申明
.claim("roles","admin")
.claim("logo","xxx.jpg");
//直接传入map
//.addClaims(map);
//获取jwt生成的token
String token = jwtBuilder.compact();
System.out.println(token);
System.out.println("============");
String[] split = token.split("\\.");
System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
//无法解密签名
System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
}
/**
* 解析token(自定义申明)
*/
@Test
public void testParseTokenByClaims() {
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiUm9zZSIsImlhdCI6MTYyNjYwODc3MCwicm9sZXMiOiJhZG1pbiIsImxvZ28iOiJ4eHguanBnIn0.qZUoGPlEELhhObwMmp9pQ3ojrc_xnNTlkTNiJLXwWrc";
//解析token得到负载中声明的对象
Claims claims = Jwts.parser()
//密钥
.setSigningKey("xxxx")
.parseClaimsJws(token)
//得到主体
.getBody();
System.out.println("id:" + claims.getId());
System.out.println("Subject:" + claims.getSubject());
System.out.println("IssuedAt:" + claims.getIssuedAt());
System.out.println("roles:" + claims.get("roles"));
System.out.println("logo:" + claims.get("logo"));
}