1. 什么是 JWT?
JWT
是 JSON Web Token 的缩写,通过数字签名的方式,以 JSON
对象为载体,在不同的服务器终端之间安全传输的信息。
2. JWT 有什么用?
JWT
最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含 JWT
,系统在每次处理用户请求之前,都先进行 JWT
安全效验,通过之后再进行处理。
JWT
就像是一个令牌,当我们去到一个公司,这个公司先回效验你的身份,效验成功后就会给你发一个身份卡,以后再见面,看见你的身份卡就知道你是这个公司的人了,就不需要再对你的身份进行认证了,上方的例子就是对 JWT
的简单解释。
3. JWT 的组成形式
JWT
主要是由三大部分组成:
3.1 Header
第一部分是头部分,一个是类型,一个是算法的名称;再通过 base64
编码进行编排
{'typ': 'JWT','alg': 'HS256'
}
3.2 Payload
第二部分是载荷,这里存储的就是有效信息的地方,这个部分又分为三个区域
1.标准中注册的声明
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
2.公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密
3.私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64
是对称解密的,意味着该部分信息可以归类为明文信息。
3.3 Signature
这个签名是:
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var = signature = HMACSH256(encodedString,'secret';)
下面就是 JWT 的三部分
xxxxx.yyyyy.zzzz
x部分:由 Header 进行 base64编码生成
y部分:由 Payload base64编码生成
z部分:由 base64编码 Header + '.' + base64编码 Payload
,再将前面的字符串使用 header
头中的算法加密生成。
4. 代码实现
4.1 生成 JWT 代码:
在创建 JwtBuilder
时,我们可以选择不创建头,当它判断 header
为空时,它会设置一个默认头,调用compact()
方法的作用就是,他会将 JWT
的三部分拼接起来,拼接成xxxx.yyyy.zzzz
的形式
//有效期为public static final Long JWT_TTL = 24*60 * 60 *1000L;// 60 * 60 *1000 一个小时//设置秘钥明文public static final String JWT_KEY = "sangeng";/*** 生成jtw* @param subject token中要存放的数据(json格式)* @param ttlMillis token超时时间* @return*/public static String createJWT(String subject, Long ttlMillis) {JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间return builder.compact();}private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;SecretKey secretKey = generalKey();long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);if(ttlMillis==null){ttlMillis=JwtUtil.JWT_TTL;}long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);return Jwts.builder().setId(uuid) //唯一的ID.setSubject(subject) // 主题 可以是JSON数据.setIssuer("zzq") // 签发者.setIssuedAt(now) // 签发时间.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥.setExpiration(expDate);}/*** 生成加密后的秘钥 secretKey* @return*/public static SecretKey generalKey() {byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}
4.2解析
我们解析出来的 Claims
对象就是之前 存入的值,我再调用对象的getSubject()
方法就可以得到我们原来所存储的值了。
/*** 生成加密后的秘钥 secretKey* @return*/public static SecretKey generalKey() {byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}/*** 解析** @param jwt* @return* @throws Exception*/public static Claims parseJWT(String jwt) throws Exception {SecretKey secretKey = generalKey();return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}