简介
什么是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.Testpublic 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.Testpublic 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*/@Testpublic 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生成的tokenString 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*/@Testpublic 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(失效时间)*/@Testpublic 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生成的tokenString 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(失效时间)*/@Testpublic 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(自定义申明)*/@Testpublic 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生成的tokenString 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(自定义申明)*/@Testpublic 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"));}
