作爲一個新手,我認爲第一步就是去看官方文檔 jjwt 別管英語有多差,有道翻譯或者網頁翻譯結果語義不盡相同,也要先看文檔,儘管我也看不懂,但是能大概知道這是做什麼的,拿來怎麼使用,然後再根據博客和一些資料進行整理,我覺得這樣能一個比較深刻的印象!
作用
官方文檔上說,JWT(JSON Web Token) 是一種可以驗證雙方之間傳遞信息的手段,並且以JSON格式呈現,JWT是可簽名的,變成JWS,或者加密,成爲JWE。
這讓使用JWT的用戶增加了一層加驗證性和安全性,例如,接收器通過驗證簽名對JWT沒有被篡改具有高度的信心。
JWT字符串格式呈現:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJudWxsaiJ9.W8hfFfcMVgmlAhTRUl4GHNAq4tq_MWJGB1bv-r9wMCE
分爲三部分,(header,payload)用Base64編碼,都是以‘ . ’進行分隔開的:
第一部分:Header:頭部,記錄了一些元數據,例如採用何種算法,令牌類型等。
第二部分:Payload:負載,存儲我們的關鍵信息。
第三部分:簽名,前兩部分的簽名,防止數據篡改。
我們主要關注 Payload,JWT 官方規定了 7 個供選擇的字段:
iss (issuer):簽發人
exp (expiration time):過期時間
sub (subject):主題
aud (audience):受衆
nbf (Not Before):生效時間
iat (Issued At):簽發時間
jti (JWT ID):編號
說白一點,到底是用來幹嘛的(個人理解):
一般在用戶登陸成功,服務端便會返回一串 字符串(同上),可以用來作爲唯一token使用,在某個時間段內失效,驗證合法性,也可以使用 jwt 來做 restful api 的身份認證,其實它就是一個標識,是每個用戶的身份證,你要做什麼,例如:開房,就必須要使用身份證才能進行,而身份證得保證合法,正確,安全。身份證到期就得去再辦一次身份證,不讓就失效了,就不可以開房了 :)
java實現
第一步,我查看文檔,jjwt框架,導入pom.xml
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
第二步創建 jwt Token創建,寫個工具類,方便以後調用。
生成key在官網上也是有講的,也給出例子,可以直接使用
/**
* 由字符串生成加密key
*
* @return
*/
private static final String KEY1 = "somethinghereshouldbelong";
public static Key generalKey() {
try {
// 一般服務端自定義一個KEY值,轉換成byte[] 再轉成SecretKey
String stringKey = "UnWhVr6cKjw5gzwnR6j5FjYpox6kRoyHbvaTwcfexb11QrKrvVeoGGP3YD3cxlKvyJL6lrK0XX0oMGcA5nPIq7ucGeUFFZ7sIuR";
byte[] encodedKey = Base64.decodeBase64(stringKey);
SecretKey key = Keys.hmacShaKeyFor(encodedKey);
//如果嫌麻煩,可以直接使用jjwt 提供key算法
// SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); //or HS384 or HS512
return key;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
* 創建新token
* @param userId
* @return
*/
public static String createNewToken(String userId){
//Header:頭部,記錄了一些元數據,例如採用何種算法,令牌類型等。
//Payload:負載,存儲我們的關鍵信息。
//Signature:簽名,前兩部分的簽名,防止數據篡改。
//Payload 官方提供7個可選字段
//iss:Issuer jwt簽發者
//sub:Subject 面向的用戶(jwt所面向的用戶)
//aud:Audience 接收jwt的一方
//exp:Expiration 過期時間戳(jwt的過期時間,這個過期時間必須要大於簽發時間)
//nbf:Not Before 定義在什麼時間之前,該jwt都是不可用的.
//iat:Issued At jwt的簽發時間
//jti:JWT ID jwt的唯一身份標識,主要用來作爲一次性token,從而回避重放攻擊。
//setClaims:自定義字段信息
Key secretKey = generalKey();
//獲取當前時間
Date now = new Date(System.currentTimeMillis());
Map<String,Object> map=new HashMap<>();
map.put("name","河神");
//過期時間
Date expiration = new Date(now.getTime() + 72000);
return Jwts
.builder()
.setClaims(map)
.setSubject("User")
.setIssuedAt(now)
.setAudience(userId)
.setIssuer("Login")
.setExpiration(expiration)
.signWith(secretKey,SignatureAlgorithm.HS512)
.compact();
}
第三步,解密,當用戶使用API時,我們需要驗證此Token是否簽名正確,才能進行服務端下一步
/**
* 解密jwt
* @param jwt 密鑰
* @return 主題
* @throws Exception 如果發生 JwtException,說明該密鑰無效
*/
public static JwtParser parseJWT (String jwt) {
Key key = generalKey();
try {
JwtParser parser = Jwts.parser();
parser
.setSigningKey(key)
.parseClaimsJws(jwt)
.getBody()
.getSubject();
return parser;
}catch (SignatureException signatureException){
throw new BusiHandlerException("簽名錯誤",10001);
}catch (ExpiredJwtException ex){
throw new BusiHandlerException("簽名過期",10001);
}catch (JwtException ex){
throw new BusiHandlerException(ex.getMessage(),ex.getCause());
}
}
異常可以自己去定義,這樣簡單的使用方式就OK了,接下來就是攔截它。
我們或許要達到此類效果:
攔截是否傳入token,附帶查看是否有註解,是否需要去驗證
根據網上的資料和博客,基本清一色的使用攔截器,這裏我想試試AOP來達到此效果。
代碼如下,不喜勿噴:
@Aspect
@Order(-1)
@Component
public class BusinessAspect {
/**
* 切入business
*/
@Pointcut("execution(public * com.hj.project.controller.*.*(..))")
public void pointCut() {
}
/**
* around
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("pointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//獲得request response
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
//獲得自定義註解
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
Token anno = method.getAnnotation(Token.class);
//如果有@Token註解
if (anno == null)
{
return joinPoint.proceed();
// throw new RuntimeException("method->" + method.getName() + " PathAuth annotation required");
}else{
String token = request.getHeader("token");
if(StringUtils.isBlank(token)){
throw new BusiHandlerException("簽名不能爲空",10001);
}
TokenUtils.parseJWT(token);
return joinPoint.proceed();
}
}
}
很簡單,是給新手的一個入門,有更好的建議,希望不吝賜教