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; } }