JWT簡介
jwt非常適合前后分離 和 分布式的應用
不必在服務端存儲session,本地也不用存儲cookie
直接存兩段信息即可
localStorage["jwt"] = jwt; // token
localStorage["name"] = json.name; // token中加密的某個字段,用於后期請求帶上校驗token是否被改
可以把認證相關的信息都存儲在里面,添加攔截去進行校驗即可
JWT認證流程:
- 用戶登錄成功,生成token,返回一個對象(包含token,用戶名)
- 每次請求都帶上這個對象(通過js存儲在電腦)
- jwt過濾器會校驗token解密之后的name是否和用戶名相同,相同則放行
- 完成(后續可能需要加上token刷新的動作)
詳細介紹:JWT 丨 JSON Web Tokens 丨 java-jwt | 詳細介紹以及用法
完整的認證demo
點擊查看 - 托管在github - 有說明
https://github.com/hisenyuan/SSM_BookSystem/tree/master/BookSystem_V3
maven依賴
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.2.0</version>
</dependency>
JWT生成與解密
package com.hisen.jars.jwt;
import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.joda.time.DateTime;
/**
* 利用java-jwt 3.2.0版本
* 每個版本的方法不大一樣
* Created by hisenyuan on 2017/8/17 at 15:41.
*/
public class Jwt {
private static final String SECRET = "XX#$%()(#*!()!KL<><MQLMNQNQJQK sdfkjsdrow32234545df>?N<:{LWPW_hisen";
private static final String EXP = "exp";
private static final String PAYLOAD = "payload";
/**
* 生成Token:jwt
* @param object 傳入的加密對象 - 放入PAYLOAD
* @param maxAge 過期事件,單位毫秒
* @param <T>
* @return
*/
public static <T> String sign(T object, long maxAge) {
Map<String, Object> map = new HashMap<String, Object>();
String jsonString = JSON.toJSONString(object);
map.put("alg", "HS256");
map.put("typ", "JWT");
long exp = System.currentTimeMillis() + maxAge;
System.out.println("JWTUtil 當前時間:"+new DateTime().toString("yyyy-MM-dd HH:mm:ss EE"));
System.out.println("JWTUtil 過期時間:"+new DateTime(exp).toString("yyyy-MM-dd HH:mm:ss EE"));
String token = null;
try {
token = JWT.create()
.withHeader(map)//header
.withClaim(PAYLOAD, jsonString)//存放的內容 json
.withClaim(EXP, new DateTime(exp).toDate())//超時時間
.sign(Algorithm.HMAC256(SECRET));//密鑰
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return token;
}
/**
* 解密token
* @param token jwt類型的token
* @param classT 加密時的類型
* @param <T>
* @return 返回解密后的對象 - 如果token過期返回空對象
*/
public static <T> T unsign(String token, Class<T> classT) {
DecodedJWT decode = JWT.decode(token);
Map<String, Claim> claims = decode.getClaims();
if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)){
long tokenTime = claims.get(EXP).asDate().getTime();
long nowTime = new Date().getTime();
// 判斷令牌是否超時
if (tokenTime > nowTime){
String json = claims.get(PAYLOAD).asString();
return JSON.parseObject(json, classT);
}
}
return null;
}
}
解密的另外一種寫法
這種寫法如果令牌超時,直接運行時異常,無法做相關處理
/**
* 解密token
* @param token jwt類型的token
* @param classT 加密時的類型
* @param <T>
* @return 返回解密后的對象 - 如果token過期返回空對象
*/
public static <T> T unsign(String token, Class<T> classT) {
JWTVerifier verifier = null;
try {
verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
DecodedJWT jwt = verifier.verify(token); // 如果超時,直接拋出運行時異常
Map<String, Claim> claims = jwt.getClaims();
if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
long tokenTime = claims.get(EXP).asDate().getTime();
long now = new Date().getTime();
// 判斷令牌是否已經超時
if (tokenTime > now) {
String json = claims.get(PAYLOAD).asString();
// 把json轉回對象,返回
return JSON.parseObject(json, classT);
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (TokenExpiredException e){
e.printStackTrace();
}
return null;
}