JWT學習(二):JWT在分散式SSO中的應用例項
阿新 • • 發佈:2019-01-07
上一篇文章講解了JWT的基本簡介,這一篇文章我就來實戰一下。介紹一下在分散式單點登入中的使用方法:
首先來看一下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簽發者 **/ private String iss; /** 該token的所有人,可以存放使用者名稱 **/ private String sub; /** 接收token的一方 **/ private String aud; /** token的過期時間(時間戳),必須要大於簽發時間;大於等於該時間需要重新整理token **/ private long exp; /** token生效的開始時間(時間戳),意味著在這個時間之前驗證token是會失敗的,預設生成token後立即生效 **/ private long nbf; /** token的簽發時間 時間戳**/ private long iat; /** token的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊 **/ private String jti; /** token驗證寬限時間(時間戳) 超過寬限時間需要重新登入, * 即該token的真正存活時間,寬限時間的加入是為了解決併發token重新整理後新token失效問題 * **/ private long gra; /** token型別: 後臺登入使用者,網際網路使用者,第三方機構 **/ 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這三部分。
然後使用者登入成功之後建立token的程式碼如下:
public static Token createToken(String secret,String tokenId,TokenType tokenType,String userName) { try { Token token = new Token(); //建立頭 Token.Head head = new Token.Head(); head.setAlg("HS256"); head.setTyp("JWT"); //建立載荷 //簽發時間 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); //簽發時間 playload.setExp(exp); playload.setGra(gra); playload.setIss("AUTH_CENTER");//簽發者 playload.setJti(tokenId); //token唯一身份標識 playload.setNbf(iat);//生效時間,立即生效 playload.setSub(userName); //token的所屬者,可以存放使用者名稱 playload.setTyp(tokenType.toString().toUpperCase()); //建立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; }
建立成功之後,要把token放到響應頭中,setHeader方法name引數要用Authorization,value值要使用
"Bearer "+token。
然後使用者訪問需要許可權的介面都需要在請求頭加上token,因為使用了Spring Cloud微服務架構,因此請求
會統一通過API閘道器訪問,所以需要在閘道器處驗證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的同學可以先看一下JWT