上一篇文章講解了JWT的基本簡介,這一篇文章我就來實戰(zhàn)一下。介紹一下在分布式單點登錄中的使用方法:
首先來看一下Token實體類,
public?class?Token?implements?Serializable{
private?static?final?long?serialVersionUID?=?-5391652691006115018L;
/**?認證頭?**/
private?Head?head;
/**?認證信息有效載荷?**/
private?Playload?playload;
/**?第一部分:base64head頭?**/
private?String?base64Head;
/**?第二部分:base64playload荷載?**/
private?String?base64PlayLoad;
/**?第三部分:簽證信息?**/
private?String?signature;
/**?最終token字符串?**/
private?String?tokenStr;
/**
?*?Token頭
?*
?*
?*/
public?static?class?Head?implements?Serializable{
private?static?final?long?serialVersionUID?=?-6516084948347601103L;
/**?token類型?**/
private?String?typ?=?"JWT";
/**?token算法?默認:HMAC?SHA256**/
private?String?alg?=?"HS256";
public?String?getTyp()?{
return?typ;
}
public?void?setTyp(String?typ)?{
this.typ?=?typ;
}
public?String?getAlg()?{
return?alg;
}
public?void?setAlg(String?alg)?{
this.alg?=?alg;
}
}
/**
?*?Token有效載荷
?*?
?*
?*/
public?static?class?Playload?implements?Serializable?{
private?static?final?long?serialVersionUID?=?3981890375700111920L;
/**?該token簽發(fā)者?**/
private?String?iss;
/**?該token的所有人,可以存放用戶名?**/
private?String?sub;
/**?接收token的一方?**/
private?String?aud;
/**?token的過期時間(時間戳),必須要大于簽發(fā)時間;大于等于該時間需要刷新token?**/
private?long?exp;
/**?token生效的開始時間(時間戳),意味著在這個時間之前驗證token是會失敗的,默認生成token后立即生效?**/
private?long?nbf;
/**?token的簽發(fā)時間?時間戳**/
private?long?iat;
/**?token的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊?**/
private?String?jti;
/**?token驗證寬限時間(時間戳)?超過寬限時間需要重新登錄,??
?*?即該token的真正存活時間,寬限時間的加入是為了解決并發(fā)token刷新后新token失效問題
?*?**/
private?long?gra;
/**?token類型:??后臺登錄用戶,互聯(lián)網(wǎng)用戶,第三方機構(gòu)?**/
private?String?typ;
public?String?getIss()?{
return?iss;
}
public?void?setIss(String?iss)?{
this.iss?=?iss;
}
public?String?getSub()?{
return?sub;
}
public?void?setSub(String?sub)?{
this.sub?=?sub;
}
public?String?getAud()?{
return?aud;
}
public?void?setAud(String?aud)?{
this.aud?=?aud;
}
public?long?getExp()?{
return?exp;
}
public?void?setExp(long?exp)?{
this.exp?=?exp;
}
public?long?getNbf()?{
return?nbf;
}
public?void?setNbf(long?nbf)?{
this.nbf?=?nbf;
}
public?long?getIat()?{
return?iat;
}
public?void?setIat(long?iat)?{
this.iat?=?iat;
}
public?String?getJti()?{
return?jti;
}
public?void?setJti(String?jti)?{
this.jti?=?jti;
}
public?long?getGra()?{
return?gra;
}
public?void?setGra(long?gra)?{
this.gra?=?gra;
}
public?String?getTyp()?{
return?typ;
}
public?void?setTyp(String?typ)?{
this.typ?=?typ;
}
}
public?Head?getHead()?{
return?head;
}
public?void?setHead(Head?head)?{
this.head?=?head;
}
public?Playload?getPlayload()?{
return?playload;
}
public?void?setPlayload(Playload?playload)?{
this.playload?=?playload;
}
public?String?getSignature()?{
return?signature;
}
public?void?setSignature(String?signature)?{
this.signature?=?signature;
}
public?String?getBase64Head()?{
return?base64Head;
}
public?void?setBase64Head(String?base64Head)?{
this.base64Head?=?base64Head;
}
public?String?getBase64PlayLoad()?{
return?base64PlayLoad;
}
public?void?setBase64PlayLoad(String?base64PlayLoad)?{
this.base64PlayLoad?=?base64PlayLoad;
}
public?String?getTokenStr()?{
return?tokenStr;
}
public?void?setTokenStr(String?tokenStr)?{
this.tokenStr?=?tokenStr;
}
}可以看到實體類里面包含了head payload signature這三部分。
然后用戶登錄成功之后創(chuàng)建token的代碼如下:
public?static?Token?createToken(String?secret,String?tokenId,TokenType?tokenType,String?userName)?{
try?{
Token?token?=?new?Token();
//創(chuàng)建頭
Token.Head?head?=?new?Token.Head();
head.setAlg("HS256");
head.setTyp("JWT");
//創(chuàng)建載荷
//簽發(fā)時間
long?iat?=?System.currentTimeMillis();
//過期時間?20?分鐘后過期
long?exp?=?iat?+?AuthConstants.TOKEN_EXP_TIME?;
//最后存活時間
long?gra?=?iat?+?AuthConstants.TOKEN_GRA_TIME?;
Token.Playload?playload?=?new?Token.Playload();
playload.setAud("CLIENT");?//接收token的一方
playload.setIat(iat);?//簽發(fā)時間
playload.setExp(exp);
playload.setGra(gra);
playload.setIss("AUTH_CENTER");//簽發(fā)者
playload.setJti(tokenId);?//token唯一身份標識
playload.setNbf(iat);//生效時間,立即生效
playload.setSub(userName);?//token的所屬者,可以存放用戶名
playload.setTyp(tokenType.toString().toUpperCase());
//創(chuàng)建token
String?base64Head?=?Base64Util.encodeStr(JSONUtil.toJson(head)?);
String?base64Playload?=?Base64Util.encodeStr(JSONUtil.toJson(playload)?);
String?signature?=?HmacUtil.encryptHMACSHA256(base64Head+"."+base64Playload,?secret);?//token簽名
String?tokenStr?=?base64Head+"."+base64Playload+"."+signature;?//token字符串
//組裝token對象
token.setHead(head);
token.setPlayload(playload);
token.setBase64Head(base64Head);
token.setBase64PlayLoad(base64Playload);
token.setSignature(signature);
token.setTokenStr(tokenStr);
return?token;
}?catch?(Exception?e)?{
e.printStackTrace();
logger.error("生成token失敗:{}",e.getMessage());
}
return?null;
}創(chuàng)建成功之后,要把token放到響應(yīng)頭中,setHeader方法name參數(shù)要用Authorization,value值要使用
"Bearer "+token。
然后用戶訪問需要權(quán)限的接口都需要在請求頭加上token,因為使用了Spring Cloud微服務(wù)架構(gòu),因此請求
會統(tǒng)一通過API網(wǎng)關(guān)訪問,所以需要在網(wǎng)關(guān)處驗證token的合法性,使用下面這個parseToken的方法:
/**
?*?驗證并解析token
?*?@param?token?token字符串
?*?@param?secret?token簽名的鹽(密鑰)
?*?@return?成功返回token?,失敗返回ull
?*/
public?static?Token?parseToken(String?token,String?secret)?{
try?{
//判斷token是否是合法格式的token串
if(StringUtils.isEmpty(token))?{
throw?new?AuthException("token串為空!");
}
if(StringUtils.isEmpty(secret))?{
throw?new?AuthException("解析token時,token密鑰為空!");
}
String[]?tokens?=?token.split("\.");
if(tokens==null?||?tokens.length!=3)?{
throw?new?AuthException("非法格式的token串:"+token);
}
//token分解
String?base64Head?=?tokens[0].trim();?//token頭
String?base64Playload?=?tokens[1].trim();?//token載荷
String?signature?=?tokens[2].trim();?//token簽名
//驗證簽名是否為合法的
String?signData?=?base64Head+"."+base64Playload;
String?signaturedStr?=?HmacUtil.encryptHMACSHA256(signData,?secret.trim());?//token簽名
if(!signature.equals(signaturedStr))?{
throw?new?AuthException("非法的token:解析token時,token驗簽失敗!");
}
Token.Head?head?=?JSONUtil.toBean(Base64Util.decodeStr(base64Head),?Token.Head.class);
Token.Playload?playLoad?=?JSONUtil.toBean(Base64Util.decodeStr(base64Playload),?Token.Playload.class);
Token?rs?=?new?Token();
rs.setHead(head);
rs.setPlayload(playLoad);
rs.setSignature(signature);
return?rs;
}?catch?(Exception?e)?{
e.printStackTrace();
logger.error("解析token失敗:{}",e.getMessage());
return?null;
}
}使用JWT進行身份驗證的基本方法的實例就到這里,沒有接觸過JWT的同學(xué)可以先看一下JWT





