package com.example.demo.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.lang3.time.DateUtils;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
*依赖:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
*
*
*
*
*/
public abstract class JWTUtil {
/**
* JWT 由3部分组成: header(Map集合),playload(负载,也可以把它看做请求体body,也是一个map集合),signature(签名,有header和playload加密后再跟secrect加密生成)
* header:有2个值,一个是类型,一个是算法,类型就是JWT,不会变,算法有2种选择,HMAC256和RS256,基本选择HMAC256
* playload:类似于post请求的请求体,是一个map集合,可以存很多很多值,如存用户的信息
* signature:由header(Base64加密后)和playload(Base64加密后)再加上secrect(秘钥生成)
* Base64加密是可逆的,所以存在header和playload的数据不能是敏感数据
*
* playload有一些值定义:
*
*
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
*
* @param userId 用户编号
* @param secrect 秘钥(密码)
* @param expireTime 过期时间单位s
* @return
*/
public static String getToken(String userId,String secrect,int expireTime){
Date createDate = new Date();
Date expireDate = DateUtils.addSeconds(createDate, expireTime);
Map<String, Object> header = new HashMap<>();
header.put("alg", "HS256");
header.put("typ", "JWT");
//token创建底层使用的是设计模式中的创建者模式,了解该模式对于下面的代码比较容易理解
String token = JWT.create().withHeader(header)
.withClaim("userId", userId) //playload的一部分:withClaim底层是一个map,可以不断使用链式表达式存数据
.withIssuedAt(createDate)//创建时间 //playload的一部分
.withExpiresAt(expireDate) //过期时间 //playload的一部分
.sign(Algorithm.HMAC256(secrect));//生成 signature
return token;
}
//如果token过期了,解析时就会报错,所以捕捉到异常时就知道是否过期了
public static DecodedJWT decodeToken(String token, String secretKey) {
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secretKey)).build();
jwt = verifier.verify(token);
return jwt;
} catch (JWTVerificationException ex) {
System.out.println("token 过期了");
throw ex;
}
}
//也可以通过token不需要密钥直接获取 DecodedJWT
public static DecodedJWT decodedToken(String token){
DecodedJWT decode = JWT.decode(token);
return decode;
//Map<String, Claim> claims = decode.getClaims();
}
//获取payLoad的值
public static Object getUserId(String token,String userId,String secrect){
DecodedJWT decodedJWT = decodeToken(token, secrect);
Map<String, Claim> claims = decodedJWT.getClaims();
Claim claim = claims.get(userId);//也可以通过claims获取其他值,具体根据存到playlaod里面的数据来取值
return claim.asString();
}
public static String login(String userName,String password){
User usr=userService.findUserByUserIdAndPassword(userName,password);
if(null==usr){
System.out.println("账号或密码错误");
return null;
}
String token = getToken(usr.getUserId,password,86400);//1天过期
token一旦生成,就没法修改,只有到过期时间后,才会失效,所以可以使用redis处理,用户每登录一次,就生成新的token
redisUtil.set("login:user:"+usr.getUserId,token,86400);//用户每登录一次就会替换一次
return token;
return null;
}
public static boolean checkToken(String userId,String token){
if(null==token){
return false;
}
String token2=redisUtil.get("login.user:"+userId);
if(!token.equal(token2)){
return false;
}
return true;
}
}