相信你們都知道 jwt(json web token)協議(若是不太瞭解能夠看 10分鐘瞭解JSON Web令牌(JWT))是目前業界經常使用於跨域身份驗證的約定方案,可是因爲 json 解析性能過低,可能產生許多性能沒法優化的問題。
· 爲了能避免 json 解析所帶來的性能損耗,咱們可使用自定義的分隔符區分不一樣元素,同時固定元素排序,達到原有 json 元素的能力。
· 同時缺省 header 部分,由於 header 部分一般用於聲明 token 格式,但若是頒發 token 和校驗 token 都是本身系統,徹底能夠缺省掉。web
Encoder 部分主要實現根據設置的 sub、exp、bnf 等元素生成 token 以下列簡要實現:json
public class SimpleJwtEncoder { /** * 生成 jwt token * 實現 sub、exp、bnf 三種元素的傳入,如需增長能夠繼續增長 * @param content * @return */ public String createToken(String content, String exp, String nbf) throws Exception { // 逗號區分 3 個元素,第一項是 sub,第二項是 exp,第三項是 nbf,不管是否有值,都會有對應的分隔符 StringBuilder sb = new StringBuilder(); sb.append(content); sb.append(SimpleJwt.PAYLOAD_SEPARATOR); if (exp != null) { sb.append(exp); } sb.append(SimpleJwt.PAYLOAD_SEPARATOR); if (nbf != null) { sb.append(nbf); } return Base64.getUrlEncoder().encodeToString(sb.toString().getBytes()) + "." + SignGenerator.genJwtSign(sb.toString()); } /** * 生成 jwt token * 省略 bnf,容許不傳 bnf 來構建 token * @param content * @return */ public String createToken(String content, String exp) throws Exception { return createToken(content, exp, ""); }
而簽名實現使用標準規範 Hmac256 也行,使用 md5 都行:跨域
public class SignGenerator { public static String genJwtSign(String content) { String originSign = content + CommonConstants.JWT_TOKEN_SECRET; return DigestUtils.md5DigestAsHex(originSign.getBytes()); } }
Decoder 須要對傳入的 token 進行解析,主要階段有 3 個:
· 轉義 payload
· 校對簽名
· 校對有效時間
針對上述三步操做的簡要實現以下:app
public class SimpleJwtDecoder { private String payload; private String sub; private LocalDateTime exp; private LocalDateTime nbf; public SimpleJwtDecoder(String token) throws Exception { String[] tokenList = StringUtils.split(token, "."); String bContent = null; String signToken = null; if (tokenList.length == 2) { bContent = tokenList[0]; signToken = tokenList[1]; } else { throw new Exception(ErrorCode.ERROR_TOKEN_DECRPTY, "token 解碼錯誤"); } byte[] contentByte = Base64.getUrlDecoder().decode(bContent); if (contentByte == null) { throw new Exception(ErrorCode.ERROR_TOKEN_DECRPTY, "content 解碼錯誤"); } String payload = new String(contentByte); // 驗證簽名是否正確 validateSign(payload, signToken); String[] payloadArray = StringUtils.split(payload, SimpleJwt.PAYLOAD_SEPARATOR); // 若是隻有兩個元素就是 sub 和 exp,再加一個元素的話就是 nbf if (payloadArray.length == 2) { setSub(payloadArray[0]); setExp(LocalDateTime.ofEpochSecond(Integer.valueOf(payloadArray[1]),0, ZoneOffset.ofHours(8))); } else if (payloadArray.length == 3) { setSub(payloadArray[0]); setExp(LocalDateTime.ofEpochSecond(Integer.valueOf(payloadArray[1]),0, ZoneOffset.ofHours(8))); setNbf(LocalDateTime.ofEpochSecond(Integer.valueOf(payloadArray[2]),0, ZoneOffset.ofHours(8))); } else { throw new Exception(ErrorCode.ERROR_TOKEN_DECRPTY, "content 解碼錯誤"); } this.payload = payload; } public void verify() throws Exception { long now = System.currentTimeMillis(); // 已超過限定時間 if (getExp() != null && now > getExp().toInstant(ZoneOffset.of("+8")).toEpochMilli()) { throw new Exception(ErrorCode.ERROR_TOKEN_DECRPTY, "token 已過時"); } // 還沒到開始時間 if (getNbf() != null && now < getNbf().toInstant(ZoneOffset.of("+8")).toEpochMilli()) { throw new Exception(ErrorCode.ERROR_TOKEN_DECRPTY, "token 還沒開始"); } } private void validateSign(String payload, String signToken) throws Exception { String sign = SignGenerator.genJwtSign(payload); if (!sign.equals(signToken)) { throw new Exception(ErrorCode.ERROR_TOKEN_DECRPTY, "驗籤失敗"); } } public String getPayload() { return payload; } public void setPayload(String payload) { this.payload = payload; } public String getSub() { return sub; } public void setSub(String sub) { this.sub = sub; } public LocalDateTime getExp() { return exp; } public void setExp(LocalDateTime exp) { this.exp = exp; } public LocalDateTime getNbf() { return nbf; } public void setNbf(LocalDateTime nbf) { this.nbf = nbf; } }