iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 29
0
Modern Web

站在Web前端人員角度,學習 Spring Boot 後端開發系列 第 29

Day 29 - Spring Boot 想要資料令牌要先帶來!- JWT

在每一次請求api都要帶上token讓後端來驗證這個請求是否是有經過認證的,若沒有驗證過就不能讓它去訪問api取得或是更改資料,請求圖如下。

https://ithelp.ithome.com.tw/upload/images/20201008/20118857OwyyNOwuVH.png

JWT(JSON Web Token)是一個網路開放標準(RFC 7519)是無狀態性的,通常利用JWT來對使用者進行身分驗證。

JWT 是一組字串,透過(.)切分成三個為 Base64 編碼的部分:

  • Header:含 Token 的種類及產生簽章(signature)要使用的雜湊演算法
  • Payload:聲明(Claim)內容,放欲存放的資訊(User的基本資訊),不要將隱私資訊存放在 Payload
  • Signature:base64編譯後的 Header、Payload 與密鑰(secret key)透過雜湊演算法所產生。

Signature 是這樣組成
HMACSHA256(
base64(header) + "." +
base64(payload),
secret)

JWT的簽發與驗證都須使用這個 secret key,當其他人得知這個 secret,那就意味著我們可以自己簽發JWT ,因此在任何情況都不應該外流。

前端存取api時必須在header帶上Authorization: Basic xxxxxx 到後端進行身分驗證

Maven dependency

<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.9.1</version>
</dependency>

創造一個JwtTokenUtils.java

@Component
public class JwtToken implements Serializable {

    private static final long EXPIRATION_TIME = 1 * 60 * 1000;
    /**
     * JWT SECRET KEY
     */
    private static final String SECRET = "learn to dance in the rain";

    /**
     * 簽發JWT
     */
    public String generateToken(HashMap<String, String> userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put( "userName", userDetails.get("userName") );

        return Jwts.builder()
                .setClaims( claims )
                .setExpiration( new Date( Instant.now().toEpochMilli() + EXPIRATION_TIME  ) )
                .signWith( SignatureAlgorithm.HS512, SECRET )
                .compact();
    }

    /**
     * 驗證JWT
     */
    public void validateToken(String token) throws AuthException {
        try {
            Jwts.parser()
                    .setSigningKey( SECRET )
                    .parseClaimsJws( token );
        } catch (SignatureException e) {
            throw new AuthException("Invalid JWT signature.");
        }
        catch (MalformedJwtException e) {
            throw new AuthException("Invalid JWT token.");
        }
        catch (ExpiredJwtException e) {
            throw new AuthException("Expired JWT token");
        }
        catch (UnsupportedJwtException e) {
            throw new AuthException("Unsupported JWT token");
        }
        catch (IllegalArgumentException e) {
            throw new AuthException("JWT token compact of handler are invalid");
        }
    }
}

有一個controller為登入request [POST]/api/login ,接收使用者登入

@RestController
@RequestMapping("/api")
public class TodoController {

@PostMapping("/login")
public ResponseEntity login(@RequestBody HashMap <String, String> user) {
    JwtToken jwtToken = new JwtToken();
    String token = jwtToken.generateToken(user); // 取得token

    return ResponseEntity.status(HttpStatus.OK).body(token);
   }
}

用POSTMAN串接[POST]/api/login 會回傳一組token
https://ithelp.ithome.com.tw/upload/images/20201008/201188579Y83hKNXav.png

可以將token放置jwt.io/ 裡去解碼出來看看token藏了哪些資訊
https://ithelp.ithome.com.tw/upload/images/20201008/20118857anHM5Tc7JP.png

假設我們現在存取某一支api [GET]/api/hello,要在header中帶入Authorization Basic tokenxxxxx ,並回傳結果。

@RestController
@RequestMapping("/api")
public class TodoController {

@GetMapping("/hello")
public ResponseEntity hello(@RequestHeader("Authorization") String au) {
    String token = au.substring(6);
    JwtToken jwtToken = new JwtToken();
    try {
        jwtToken.validateToken(token);
    } catch (AuthException e) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage());
    }

    return ResponseEntity.status(HttpStatus.OK).body("Hello CaiLi");
  }
}

接著用POSTMAN存取[GET]/api/hello ,回傳status code 200 OK
https://ithelp.ithome.com.tw/upload/images/20201008/20118857N89uC5qtMb.png

因為token 的過期時間為一分鐘,一分鐘後再請求一次,則會回傳403 Forbidden
https://ithelp.ithome.com.tw/upload/images/20201008/20118857VmRhfjS4GK.png

Reference

JWT(JSON Web Token) — 原理介紹
Spring Boot Security 整合JWT實現無狀態的分散式API介面


上一篇
Day 28 - Spring Boot Security 守護安全
下一篇
Day 30 - Spring Boot 新手學習,完賽!
系列文
站在Web前端人員角度,學習 Spring Boot 後端開發30

1 則留言

0
Devin
iT邦新手 5 級 ‧ 2020-10-08 23:45:54

恭喜恭喜 只剩下一天了!

我要留言

立即登入留言