基於token的身份驗證JWT


一、概念:

  JWT:Json Web Token。JWT 是JSON風格輕量級的授權和身份認證規范,可實現無狀態、分布式的Web應用授權。是基於token的一種授權認證方式。就是一個字符串,經過加密處理與校驗處理的字符串。JWT可以使用HMAC算法或者是RSA的公私秘鑰對進行簽名。簡潔(Compact): 可以通過URL,POST參數或者在HTTP header發送,因為數據量小,傳輸速度也很快 自包含(Self-contained):負載中包含了所有用戶所需要的信息,避免了多次查詢數據庫。

二、token應用流程為:

1、初次登錄:用戶初次登錄,輸入用戶名密碼。

2、密碼驗證:服務器從數據庫取出用戶名和密碼進行驗證。

3、生成JWT:服務器端驗證通過,根據從數據庫返回的信息,以及預設規則,生成JWT。

4、返還JWT:服務器的HTTP RESPONSE中將JWT返還。

5、帶JWT的請求:以后客戶端發起請求,HTTP REQUEST HEADER中的Authorization字段都要有值,為JWT,用來驗證用戶身份以及對路由,服務和資源的訪問權限進行驗證。請求驗證的url可以例如:http://127.0.0.1:8083/change/goodsMenu? token=JWT

三、JWT的結構

JWT包含了使用.分隔的三部分: Header 頭部 Payload 負載 Signature 簽名,它的結構是這樣的Header.Payload.Signature,調用createJWT()方法生成的一個JWT如下:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdGFmZklkIjoiTFRIWFkxMzAiLCJwcm92aW5jZSI6IjEzIiwiZXhwIjoxNTI5NjM1ODE2LCJuYmYiOjE1Mjk2MzU4MTZ9.Sne5T_Iqx2NQdZIoVpaqHLT2HCjf0AbKEgEhbbNcu_0,是經過加密和編碼之后生成的一串字符串,中間以.來分割,共三個部分:

Header:

Header格式為:

{

    "typ": "JWT",

    "alg": "HS256"

}

在header中通常包含了兩部分:token類型和采用的加密算法。它就是一個json串,兩個字段是必須的,不能多也不能少。alg字段指定了生成JWT的算法,默認值是HS256,生成JWT的算法可以不指定,默認為HS256,對這部分內容使用 Base64Url 編碼組成了JWT結構的第一部分。

Payload:

Token的第二部分是負載,也就是需要交換的實際數據,它包含了claim, Claim是一些實體(通常指的用戶)的狀態和額外的需要傳遞的元數據。claim set是一個json數據,是表明用戶身份的數據,可自行指定字段很靈活,也有固定字段表示特定含義。固定字段(見下圖claims截圖)有 iss(簽發者),exp(過期時間戳), sub(面向的用戶), aud(接收方), iat(簽發時間),jti(JWT ID,針對當前token的唯一標識),nbf(not before,如果當前時間在nbf里的時間之前,則Token不被接受,一般都會留一些余地,比如幾分鍾)。將claim set加密后得到負載,經過Base64Url編碼后作為JWT結構的第二部分。claims類的如下:

public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> { /** JWT {@code Issuer} claims parameter name: <code>"iss"</code> */
    public static final String ISSUER = "iss"; /** JWT {@code Subject} claims parameter name: <code>"sub"</code> */
    public static final String SUBJECT = "sub"; /** JWT {@code Audience} claims parameter name: <code>"aud"</code> */
    public static final String AUDIENCE = "aud"; /** JWT {@code Expiration} claims parameter name: <code>"exp"</code> */
    public static final String EXPIRATION = "exp"; /** JWT {@code Not Before} claims parameter name: <code>"nbf"</code> */
    public static final String NOT_BEFORE = "nbf"; /** JWT {@code Issued At} claims parameter name: <code>"iat"</code> */
    public static final String ISSUED_AT = "iat"; /** JWT {@code JWT ID} claims parameter name: <code>"jti"</code> */
    public static final String ID = "jti";

Signature:

創建簽名需要使用編碼后的header和payload以及一個秘鑰,使用header中指定簽名算法進行簽名。例如如果希望使用HMAC SHA256算法,那么簽名應該使用下列方式創建: HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 簽名用於驗證消息的發送者以及消息是沒有經過篡改的。完整的JWT格式的輸出是以 . 分隔的三段Base64編碼,JWT在HTTP和HTML環境中更容易傳遞。簽名其實就是一個字符串。作用類似於CRC校驗,保證加密沒有問題。

常見錯誤:解析token報,說明token傳入的格式不對,不符合JWT的正確格式:

io.jsonwebtoken.MalformedJwtException: JWT strings must contain exactly 2 period characters. Found: 0

io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:235)

io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)

io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541)

JWT工具類代碼:

public class JWT { /** * @Title: generalKey * @Description: 獲取解析JWT和生成JWT的秘鑰 * @param @param fromSys * @param @return 
    */
    public static SecretKey generalKey(String fromSys) { String stringKey = new Miner().getMiner("tokenKey").getString(fromSys); byte[] encodedKey = Base64.decodeBase64(stringKey); SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } public static Claims parseJWT(String jwt, String fromSys) { SecretKey key = generalKey(fromSys); Claims claims = Jwts.parser().setSigningKey(key).setAllowedClockSkewSeconds(3600 * 24).parseClaimsJws(jwt) .getBody(); return claims; } public static String createJWT(String staffId, String province, long expiresSecond , Claims claims, String fromSys){ SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); // 生成簽名密鑰
        SecretKey key = generalKey(fromSys); JwtBuilder builder = Jwts.builder() // 設置header, 可以調用setHeaderParams()方法同時設置token類型和加密算法,加密的默認值是HS256
            .setHeaderParam("typ", "JWT") // 添加構成JWT的參數, 自定義claim的屬性, 也可以調用setId和setSubject等類自身方法
            .claim("staffId", staffId) .claim("province", province) .signWith(signatureAlgorithm, key); // 添加claims信息
        if(claims != null && !claims.isEmpty()) { builder.setClaims(claims); } // 添加Token過期時間
        if (expiresSecond  >= 0) { long expMillis = nowMillis + expiresSecond ; Date exp = new Date(expMillis); builder.setExpiration(exp).setNotBefore(now); } return builder.compact(); } }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM