JWT詳解

1、JWT介紹

定義:JSON Web Token(縮寫 JWT)是目前最流行的跨域認證解決方案
JWT官網:https://jwt.io/

由於HTTP協議是無狀態的,如果想判定一個接口是否被認證後訪問,就需要藉助cookie或者session會話機制進行判定,但是由於現在的系統架構不止一臺服務器,此時要藉助數據庫或者全局緩存做存儲,這種方案受限太多。
JWT提供提供了一種更好的方案,由令牌發佈者發佈令牌,其他服務持有令牌,當接口訪問時需要提供令牌,並由令牌發佈者去驗證這個令牌。優點是簡單快捷、不需要依賴任何第三方就能實現身份認證,缺點是任何持有令牌的用戶都可以認證通過。

2、JWT的數據結構

正常的JWT數據結構如下:
截圖
JWT是一段字符串,中間由.分割爲三部分。
分別是
Header:頭部
Payload:負載
Payload:負載
JWT的格式即爲:Header.Payload.Signature

2.1、Header頭部

Header部分是一個JSON對象,描述JWT的元數據。
格式如下:
{
“alg”: “HS256”,
“typ”: “JWT”
}
alg屬性表示簽名的算法(algorithm),默認值爲HMAC SHA256(HS256);
typ屬性表示這個令牌(token)的類型,JWT的令牌值爲"JWT"。

2.2、Payload負載

Payload 部分也是一個 JSON 對象,用來存放實際需要傳遞的數據。JWT 規定了7個官方字段。
iss (issuer):簽發人

exp (expiration time):過期時間

sub (subject):主題

aud (audience):受衆

nbf (Not Before):生效時間

iat (Issued At):簽發時間

jti (JWT ID):編號
除了官方字段還可以使用withClaim(“name”, name)定義私有字段。
例如
{
“name”:“zhansan”
}

注意:JWT默認是明文展示,任何人都可以讀取到,所以此處不要放私密信息。

2.3、Signature簽名

Signature 部分是對前兩部分的簽名,防止數據篡改。
指定一個密鑰(secret),使用Header裏面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產生簽名。
HMACSHA256(base64UrlEncode(header) + “.” +base64UrlEncode(payload),secret);
得到簽名後,把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用(.)分隔,並返回給用戶。

2.4、Base64URL字符串編碼

上文提到Header和Payload字符編碼採用的是Base64URL,和Base64算法類似。Base64算法中還有"+","/","=“字符串,在URL裏面有特殊含義,所以要被替換掉:”=“省略、”+“替換成”-","/“替換成”_",這就是Base64URL算法。

3、JWT實現應用

Maven依賴

<dependency>
     <groupId>com.auth0</groupId>
     <artifactId>java-jwt</artifactId>
     <version>3.8.0</version>
</dependency>

實現代碼

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class TokenUtil {
    private static Logger logger = LoggerFactory.getLogger(TokenUtil.class);
    /**
     * Token的過期時間
     */
    private static final Integer EXPIRE_TIME = 24 * 60 * 60;
    /**
     * Token的私鑰
     */
    private static final String TOKEN_SECRET = "token_secret";

    public static String createToken(String userName) {
        try {
            // 設置過期時間
            Calendar nowTime = Calendar.getInstance();
            nowTime.add(Calendar.SECOND, EXPIRE_TIME);
            Date expireDate = nowTime.getTime();
            //私鑰和加密算法
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            // 設置頭部信息
            Map<String, Object> header = new HashMap<>();
            header.put("typ", "JWT");
            header.put("alg", "HS256");
            // 返回token字符串
            return JWT.create()
                    .withHeader(header)
                    .withClaim("userName", userName)
                    .withIssuedAt(new Date())
                    .withExpiresAt(expireDate)
                    .sign(algorithm);
        } catch (Exception e) {
            logger.error("CreateToken Error", e);
            return null;
        }
    }

    /**
     * 檢驗token是否正確
     */
    public static boolean verify(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            //未驗證通過會拋出異常
            verifier.verify(token);
            return true;
        } catch (Exception e) {
            logger.error("verify Error", e);
            return false;
        }
    }

    /**
     * 獲取自定字段
     */
    public static Map<String, Claim> getClaims(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT decodedJWT = verifier.verify(token);
            return decodedJWT.getClaims();
        } catch (Exception e) {
            logger.error("getClaims Error", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 獲取JWT
     */
    public static DecodedJWT getDecoded(String token) {
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).build();
            DecodedJWT jwt = verifier.verify(token);
            return jwt;
        } catch (UnsupportedEncodingException e) {
            logger.error("getDecoded Error", e);
            return null;
        }
    }

    public static void main(String[] args) {
        String token = TokenUtil.createToken("zhansan");
        System.out.println(TokenUtil.verify(token));
        Map<String, Claim> claims = TokenUtil.getClaims(token);
        Claim userName = claims.get("userName");
        System.out.println(userName.asString());
        DecodedJWT jwt = TokenUtil.getDecoded(token);
        System.out.println(jwt.getType());
        System.out.println(jwt.getPayload());
        System.out.println(jwt.getSignature());
        System.out.println(jwt.getToken());
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章