JWT入門級,簡單易用

作爲一個新手,我認爲第一步就是去看官方文檔 jjwt 別管英語有多差,有道翻譯或者網頁翻譯結果語義不盡相同,也要先看文檔,儘管我也看不懂,但是能大概知道這是做什麼的,拿來怎麼使用,然後再根據博客和一些資料進行整理,我覺得這樣能一個比較深刻的印象!

作用

官方文檔上說,JWT(JSON Web Token) 是一種可以驗證雙方之間傳遞信息的手段,並且以JSON格式呈現,JWT是可簽名的,變成JWS,或者加密,成爲JWE。
這讓使用JWT的用戶增加了一層加驗證性和安全性,例如,接收器通過驗證簽名對JWT沒有被篡改具有高度的信心。
JWT字符串格式呈現:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJudWxsaiJ9.W8hfFfcMVgmlAhTRUl4GHNAq4tq_MWJGB1bv-r9wMCE

分爲三部分,(header,payload)用Base64編碼,都是以‘ . ’進行分隔開的:

	第一部分:Header:頭部,記錄了一些元數據,例如採用何種算法,令牌類型等。
	第二部分:Payload:負載,存儲我們的關鍵信息。
	第三部分:簽名,前兩部分的簽名,防止數據篡改。

我們主要關注 Payload,JWT 官方規定了 7 個供選擇的字段:

	iss (issuer):簽發人
	exp (expiration time):過期時間
	sub (subject):主題
	aud (audience):受衆
	nbf (Not Before):生效時間
	iat (Issued At):簽發時間
	jti (JWT ID):編號

說白一點,到底是用來幹嘛的(個人理解):

一般在用戶登陸成功,服務端便會返回一串 字符串(同上),可以用來作爲唯一token使用,在某個時間段內失效,驗證合法性,也可以使用 jwt 來做 restful api 的身份認證,其實它就是一個標識,是每個用戶的身份證,你要做什麼,例如:開房,就必須要使用身份證才能進行,而身份證得保證合法,正確,安全。身份證到期就得去再辦一次身份證,不讓就失效了,就不可以開房了 :)

java實現

第一步,我查看文檔,jjwt框架,導入pom.xml

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.10.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.10.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.10.5</version>
    <scope>runtime</scope>
</dependency>

第二步創建 jwt Token創建,寫個工具類,方便以後調用。
生成key在官網上也是有講的,也給出例子,可以直接使用

  /**
     * 由字符串生成加密key
     *
     * @return
     */
    private static final String KEY1 = "somethinghereshouldbelong";
    public static Key generalKey() {
        try {
            // 一般服務端自定義一個KEY值,轉換成byte[] 再轉成SecretKey
            String stringKey = "UnWhVr6cKjw5gzwnR6j5FjYpox6kRoyHbvaTwcfexb11QrKrvVeoGGP3YD3cxlKvyJL6lrK0XX0oMGcA5nPIq7ucGeUFFZ7sIuR";
            byte[] encodedKey = Base64.decodeBase64(stringKey);
            SecretKey key = Keys.hmacShaKeyFor(encodedKey);

            //如果嫌麻煩,可以直接使用jjwt 提供key算法
//            SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); //or HS384 or HS512
            return key;

        }catch (Exception e){
            e.printStackTrace();
        }
      return null;
    }
/**
     * 創建新token
     * @param userId
     * @return
     */
    public static   String createNewToken(String userId){
        //Header:頭部,記錄了一些元數據,例如採用何種算法,令牌類型等。
        //Payload:負載,存儲我們的關鍵信息。
        //Signature:簽名,前兩部分的簽名,防止數據篡改。

        //Payload 官方提供7個可選字段
        //iss:Issuer  jwt簽發者
        //sub:Subject  面向的用戶(jwt所面向的用戶)
        //aud:Audience  接收jwt的一方
        //exp:Expiration  過期時間戳(jwt的過期時間,這個過期時間必須要大於簽發時間)
        //nbf:Not Before 定義在什麼時間之前,該jwt都是不可用的.
        //iat:Issued At jwt的簽發時間
        //jti:JWT ID jwt的唯一身份標識,主要用來作爲一次性token,從而回避重放攻擊。
        //setClaims:自定義字段信息
            Key secretKey = generalKey();
        //獲取當前時間
        Date now = new Date(System.currentTimeMillis());
        Map<String,Object> map=new HashMap<>();
        map.put("name","河神");
        //過期時間
        Date expiration = new Date(now.getTime() + 72000);
            return Jwts
                    .builder()
                    .setClaims(map)
                    .setSubject("User")
                    .setIssuedAt(now)
                    .setAudience(userId)
                    .setIssuer("Login")
                    .setExpiration(expiration)
                    .signWith(secretKey,SignatureAlgorithm.HS512)
                    .compact();
    }

第三步,解密,當用戶使用API時,我們需要驗證此Token是否簽名正確,才能進行服務端下一步

/**
     * 解密jwt
     * @param jwt 密鑰
     * @return 主題
     * @throws Exception 如果發生 JwtException,說明該密鑰無效
     */
    public static JwtParser parseJWT (String jwt)  {
        Key key = generalKey();
        try {
            JwtParser parser = Jwts.parser();
                    parser
                    .setSigningKey(key)
                    .parseClaimsJws(jwt)
                    .getBody()
                    .getSubject();
                    return parser;
        }catch (SignatureException signatureException){
            throw new BusiHandlerException("簽名錯誤",10001);
        }catch (ExpiredJwtException ex){
            throw new BusiHandlerException("簽名過期",10001);
        }catch (JwtException ex){
          throw new BusiHandlerException(ex.getMessage(),ex.getCause());
        }
    }

異常可以自己去定義,這樣簡單的使用方式就OK了,接下來就是攔截它。

我們或許要達到此類效果:
攔截是否傳入token,附帶查看是否有註解,是否需要去驗證
根據網上的資料和博客,基本清一色的使用攔截器,這裏我想試試AOP來達到此效果。
代碼如下,不喜勿噴:

@Aspect
@Order(-1)
@Component
public class BusinessAspect {


    /**
     * 切入business
     */
    @Pointcut("execution(public * com.hj.project.controller.*.*(..))")
    public void pointCut() {
    }

    /**
     * around
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //獲得request response
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();

        //獲得自定義註解
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Token anno = method.getAnnotation(Token.class);
         //如果有@Token註解
       if (anno == null)
        {
            return joinPoint.proceed();
//            throw new RuntimeException("method->" + method.getName() + " PathAuth annotation required");
        }else{
           String token = request.getHeader("token");
           if(StringUtils.isBlank(token)){
               throw new BusiHandlerException("簽名不能爲空",10001);
           }
           TokenUtils.parseJWT(token);
           return joinPoint.proceed();
       }
    }


}

很簡單,是給新手的一個入門,有更好的建議,希望不吝賜教

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章