1.JWT(Json Web Token)
JSON Web token简称JWT(令牌-字符串), 是用于对应用程序上的用户进行身份验证的标记。JWT令牌由Header(头部)、Payload(负载)和 Signature(签名)三部分组成,本质就是一个字符串。每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz
Header(头部)
头部包括令牌的类型(即JWT)及使用的哈希算法(如HMAC、SHA256或RSA),主要声明了JWT的签名算法。
Payload(负载)
第二部分是负载,内容也是一个Json对象,它是存放有效信息的地方,它可以存放JWT提供的现成字段,比 如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。 此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。主要承载了各种声明并传递明文数据。
Signature(签名)
第三部分是签名,此部分用于防止JWT内容被篡改。 这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明 签名算法进行签名。
Signature(签名)是一个签证信息,这个签证信息由三部分组成:
base64UrlEncode(header):JWT令牌的第一部分。
base64UrlEncode(payload):JWT令牌的第二部分。
secret:签名所使用的密钥(私钥)。
JWT就是一个字符串,经过加密处理与校验处理的字符串,形式为:A.B.C
A由JWT头部信息header编码(解码)得到,是一个Json 字符串,包含jwt的版本,签名算法
B由JWT用到的身份验证信息Json数据编码(解码)得到 叫载荷 自定义存储的数据
C由A和B+秘钥加密得到,是校验部分 signature 验证header与playload有没有被修改过
JwtUtils工具类
package com.commons.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtils {
// TOKEN的有效期1小时(S)
private static final int TOKEN_TIME_OUT = 1 * 3600;
// 加密KEY
private static final String TOKEN_SECRET = "jwtToken";
// 生成Token
public static String getToken(Map params){
long currentTime = System.currentTimeMillis();
return Jwts.builder()
// 参数1:算法名;参数2:加盐
.signWith(SignatureAlgorithm.HS512, TOKEN_SECRET) //加密方式
// 设置token有效期时间
.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //过期时间戳
// 参数:map集合
.addClaims(params)
.compact();
}
/**
* 获取Token中的claims信息
*/
public static Claims getClaims(String token) {
return Jwts.parser()
.setSigningKey(TOKEN_SECRET)
.parseClaimsJws(token).getBody();
}
/**
* 是否有效 true-有效,false-失效
*/
public static boolean verifyToken(String token) {
if(StringUtils.isEmpty(token)) {
return false;
}
try {
Claims claims = Jwts.parser()
.setSigningKey("jwtToken")
.parseClaimsJws(token)
.getBody();
}catch (Exception e) {
return false;
}
return true;
}
}
JWT的主要目的是在服务端和客户端之间以安全的方式来转移声明。
主要的应用场景如下所示:
- 认证 Authentication;
- 授权 Authorization;
- 联合识别;
- 客户端会话(无状态的会话);
- 客户端机密。
2.JWS、JWE、JWK、JWKset、JWA和nonsecure JWT区别
JWS | JSON Web Signature(签名) | 拥有Signature(签名)部分的JWT被称为JWS,也就是 JWT签名的JWS,也就是JWT Signature |
JWE | JSON Web Encryption(加密) | JWT部分payload经过加密的JWT |
JWK | JSON Web Key | JWT的密钥(私钥),也就是我们常说的 scret |
JWKset | JSON Web Key set | JWT key set在非对称加密中,需要的是密钥对而非单独的密钥,在后文中会阐释 |
JWA | JSON Web Algorithms(算法) | 当前JWT所用到的密码学算法 |
nonsecure JWT | 当头部的签名算法被设定为none的时候,该JWT是不安全的;因为签名的部分空缺,所有人都可以修改 | 没有Signature(签名)部分的JWT被称为nonsecure JWT,也就是不安全的JWT |
JJWT | 用于在JVM上创建和验证JSON Web令牌(JWTs)的库 | 是基于JWT、JWS、JWE、JWK和JWA RFC规范的Java实现(是提供端到端的JWT创建和验证的Java库) |
3.JWT用户认证工作原理
1.客户端使用用户名和密码请求登录
2.服务端收到请求,验证用户名和密码
3.验证成功后,服务端会签发一个token,再把这个token返回给客户端
4.客户端收到token后可以把它存储起来,比如放到cookie中
5.客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带
6.服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据
4.实现JWT生成token
4.1在Maven工程中pom.xml中添加JWT相关依赖
<!--JWT令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
4.2JWT工具类
import com.SystemConstants;
import io.jsonwebtoken.*;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;
public class AppJwtUtil {
// TOKEN的有效期时间(S)
private static final int TOKEN_TIME_OUT = 3600;
// 加密KEY
private static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";
// 最小刷新间隔(S)
private static final int REFRESH_TIME = 300;
// 生产ID
public static String createToken(Long id) {
Map<String, Object> claimMaps = new HashMap<>();
claimMaps.put("id", id);
long currentTime = System.currentTimeMillis();
return Jwts.builder()
.setId(UUID.randomUUID().toString())
.setIssuedAt(new Date(currentTime)) //签发时间
.setSubject("system") //说明
.setIssuer("it") //签发者信息
.setAudience("app") //接收用户
.compressWith(CompressionCodecs.GZIP) //数据压缩方式
.signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
//过期一个小时
.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //过期时间戳
.addClaims(claimMaps) //cla信息
.compact();
}
/**
* 获取token中的claims信息
* @param token
* @return
*/
private static Jws<Claims> getJws(String token) {
return Jwts.parser()
.setSigningKey(generalKey())
.parseClaimsJws(token);
}
/**
* 获取payload body信息
* @param token
* @return
*/
public static Claims getClaimsBody(String token) {
try {
return getJws(token).getBody();
} catch (ExpiredJwtException e) {
return null;
}
}
/**
* 获取hearder body信息
* @param token
* @return
*/
public static JwsHeader getHeaderBody(String token) {
return getJws(token).getHeader();
}
/**
* 是否过期
* @param token
* @return 1 有效 0 无效 2 已过期
*/
public static Integer verifyToken(String token) {
try {
Claims claims = AppJwtUtil.getClaimsBody(token);
if (claims == null) {
return SystemConstants.JWT_FAIL;
}
return SystemConstants.JWT_OK;
} catch (ExpiredJwtException ex) {
return SystemConstants.JWT_EXPIRE;
} catch (Exception e) {
return SystemConstants.JWT_FAIL;
}
}
/**
* 由字符串生成加密key
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
}
4.3JWT系统常量类
public class SystemConstants {
//JWT TOKEN已过期
public static final Integer JWT_EXPIRE = 2;
//JWT TOKEN有效
public static final Integer JWT_OK = 1;
//JWT TOKEN无效
public static final Integer JWT_FAIL = 0;
}