- 一、JWT是什么
- 二、JWT构成
- 三、SpringBoot集成JWT
什么是JWT:Json web token (JWT) 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519)。定义了一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA或ECDSA的公私秘钥对进行签名。
JWT如何获取访问令牌(token)并用于访问资源(API)流程:1、应用端向权限服务器请求授权;2、权限服务器授权成功向应用端返回一个访问令牌(token);3、应用端使用访问令牌(token)访问受保护的资源(如API)。
二、JWT构成2.1 JWT是由三段信息构成,将这三段信息文本用“.“连接一起就构成了JWT字符串,一个 Token 分三部分,按顺序为 1.头部(header) 2.载荷(payload) 3.签证(signature) 例如:`eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE1NTgwNjI2OTYsInVzZXJJZCI6IjEifQ.XiI0xjX0izVeJRmhbXN1w1fXKdHB0wsc9teFKq84pclpJt6yS2k0BVXAklHrke_nz6XtcCyi1hgvpn8bf95gwg`。 2.2 header JWT的头部承载两部分信息: 1.声明类型,这里是JWT 2.声明加密的算法,通常直接使用 HMAC SHA256 JWT里验证和签名使用的算法列表如下:
| JWT | 算法名称 |
|---|---|
| HS256 | HMAC256 |
| HS384 | HMAC384 |
| HS512 | HMAC512 |
| RS256 | RSA256 |
| RS384 | RSA384 |
| RS512 | RSA512 |
| ES256 | ECDSA256 |
| ES384 | ECDSA384 |
| ES512 | ECDSA512 |
2.3 playload 载荷就是存放有效信息的地方。基本上填两种类型的数据 1.标准中注册的声明的数据; 2.自定义数据; 由这两部分内部做 base64 加密。 标准中注册的声明(建议但不强制使用)
iss: jwt签发者 sub: jwt所面向的用户 aud: 接收jwt的一方 exp: jwt的过期时间,这个过期时间必须要大于签发时间 nbf: 定义在什么时间之前,该jwt都是不可用的. iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 自定义数据:存放我们想放在 token 中存放的 key-value 值;
2.4 signature JWT的第三部分是一个签证信息,这个签证信息由三部分组成; base64 加密后的 header 和 base64 加密后的 payload 连接组成的字符串,然后通过 header 中声明的加密方式进行加盐 secret 组合加密,然后就构成了JWT的第三部分三、SpringBoot集成JWT
3.1 添加依赖:
com.auth0 java-jwt3.3.0 io.jsonwebtoken jjwt0.9.0
3.2 创建常量类:
public interface Constants {
public static String JWT_SECERT = "SSO_JWT_SECERT_KEY";
public static String JWT_ISS_USER = "sxt-frugal-jwt";
public static int JWT_FAILED_CODE = 500;
public static int JWT_EXPIRE_CODE = 504;
public static int JWT_LOGIN_MILLIS = 10 * 60 * 1000;
public static String JWT_HEAD_KEY = "Authorization";
public static String PASSWORD_KEY = "password_salt_key";
public static int SSO_LOGIN_SECOND = 60 * 30;
}
3.3 创建工具类
@Slf4j
public class JWTUtils {
public static final ObjectMapper mapper = new ObjectMapper();
private static SecretKey generalKey(String algorithm) {
byte[] secreKey = base64.decode(Constants.JWT_SECERT);
return new SecretKeySpec(secreKey, 0, secreKey.length, algorithm);
}
public static String createJwt(String id, String iss, String claims, long ttlmillis) {
SignatureAlgorithm algorithm = SignatureAlgorithm.HS256;
long timeMillis = System.currentTimeMillis();
Date curDate = new Date(timeMillis);
JwtBuilder jwtBuilder = Jwts.builder().setId(id).setIssuer(iss).setSubject(claims)
.setIssuedAt(curDate)
.signWith(algorithm, JWTUtils.generalKey("AES"));
if (ttlmillis > 0) {
jwtBuilder.setExpiration(new Date(ttlmillis + timeMillis));
}
return jwtBuilder.compact();
}
public static JwtEntity validateToken(String token) {
JwtEntity jwtEntity = new JwtEntity();
Claims claims = null;
try {
claims = JWTUtils.parseToken(token);
jwtEntity.setStatus(true);
jwtEntity.setClaims(claims);
} catch (Exception e) {
log.error("jwt解析异常:", e);
jwtEntity.setStatus(false);
jwtEntity.setCode(Constants.JWT_FAILED_CODE);
}
return jwtEntity;
}
private static Claims parseToken(String token) {
SecretKey generaKey = JWTUtils.generalKey("AES");
return Jwts.parser().setSigningKey(generaKey).parseClaimsJwt(token).getBody();
}
public static String generalSubject(Object object) throws JsonProcessingException {
return mapper.writevalueAsString(object);
}
public static String reInitJwt(JwtEntity jwtEntity) {
Claims claims = jwtEntity.getClaims();
return JWTUtils.createJwt(String.valueOf(System.nanoTime()), Constants.JWT_ISS_USER, claims.getSubject(),
Constants.JWT_LOGIN_MILLIS);
}
3.4 JWT工具类调用
public ResponseEntity getLogin(String phone, String password) {
Map login = loginMapper.selectOne(phone);
if (MapUtil.isEmpty(login)) {
return ResponseEnum.FAILED.fail("该用户不存在!");
}
String mdPwd = new HmacUtils(HmacAlgorithms.HMAC_MD5, Constants.PASSWORD_KEY).hmacHex(password);
if (mdPwd.equals(login.get("password"))) {
login.remove("password");
String claims = null;
try {
claims = JWTUtils.generalSubject(login);
} catch (JsonProcessingException e) {
log.error("claims 解析json异常!", e);
e.printStackTrace();
}
String jwt = JWTUtils.createJwt(String.valueOf(System.nanoTime()), phone, claims,
Constants.JWT_LOGIN_MILLIS);
response.addHeader(Constants.JWT_HEAD_KEY, jwt);
JedisTemplate.getInstance().setStringEx(ThreadUtils.getCurrentThreadId(), Constants.SSO_LOGIN_SECOND, phone);
JedisTemplate.getInstance().setHashEx(phone, Constants.SSO_LOGIN_SECOND, login);
return ResponseEnum.SUCCESSED.success(login);
}
return ResponseEnum.FAILED.fail("用户名或密码错误!");
}



