iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0
Mobile Development

Spring Boot & Android Studio教學系列 第 19

掌握Spring Security:使用JWT實現身份驗證(EP2)

  • 分享至 

  • xImage
  •  

JWT(JSON Web Token)

是一種用於在網絡上安全傳輸信息的開放標準。它通常用於身份驗證和授權方面的應用,尤其在Web應用程序和移動應用程序中廣泛使用。JWT的主要特點是它是一種緊湊的、自包含的數據格式,可以在各種系統之間輕鬆傳遞信息,同時確保數據的完整性和安全性。

以下是JWT的一些關鍵特點和組成部分:

JSON格式:

JWT使用JSON(JavaScript Object Notation)作為其數據格式,這使得它易於理解和使用。

自包含性:

JWT包含了所有必要的信息,因此不需要在伺服器上存儲會話信息或狀態信息。這減輕了伺服器的負擔,並使應用程序可以更容易地擴展。

數字簽名:

JWT可以使用數字簽名進行驗證,以確保其內容在傳輸過程中未被篡改。這有助於確保數據的完整性和安全性。

JWT通常由三部分組成,它們以句點(.)分隔開來:

Header:

包含了有關令牌的元數據,例如使用的簽名算法(例如HS256或RS256)。

Payload:

包含了令牌的主要信息,通常包括使用者ID、過期時間、發行者等等。

Signature:

用於驗證令牌的簽名部分,確保令牌在傳輸過程中沒有被篡改。

JWT的工作流程:

  1. 使用者登錄並提供身份驗證憑證(例如使用者名稱和密碼)。
  2. 伺服器驗證憑證,並生成一個JWT,將使用者信息包含在Payload中,並使用私鑰簽名令牌。
  3. 伺服器將JWT返回給客戶端。
  4. 客戶端在後續請求中使用JWT,將其包含在請求標頭或Cookie中。
  5. 伺服器收到請求後,驗證JWT的簽名以確保其完整性,然後使用其中的信息來驗證使用者身份和授權。

實作:

延續上一篇

依賴

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
</dependency>

JwtService.java

這裡將處理jwt相關作業,如:提取用戶,解析聲明與生成token等等

@Service
@Slf4j
public class JwtService {
    // Token有效期限 (設定15分鐘過期)
    private Long EXPIRATION_TIME = 60*60*1000; //單位ms

    //BASE64編碼的密鑰
    private String SECRET_KEY = "546A55A71347A254462D4......";

    /**
     * 從JWT令牌中提取用戶名
     */
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * 提取JWT令牌中的任何聲明(Claims),並通過提供的Function來解析它們。
     */
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    public String generateToken(UserDetails userDetails) {
        return generateToken(new HashMap<>(), userDetails);
    }

    /**
     * 簽發Token
     */
    public String generateToken(
        Map<String, Object> extractClaims,
        UserDetails userDetails
    ) {
        return Jwts
            .builder()
            .setClaims(extractClaims)
            .setSubject(userDetails.getUsername()) //以Username做為Subject
            .setIssuedAt(new Date(System.currentTimeMillis()))
            .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
            .signWith(getSignInKey(), SignatureAlgorithm.HS256)
            .compact();
    }

    /**
     * 驗證Token有效性,比對JWT和UserDetails的Username(Email)是否相同
     * @return 有效為True,反之False
     */
    public boolean isTokenValid(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
    }

    /**
     * 驗證token是否過期
     */
    private boolean isTokenExpired(String token) {
        final Date expirationDate = extractExpiration(token);
//        return extractExpiration(token).before(new Date());
        return expirationDate != null && expirationDate.before(new Date());
    }

    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    /**
     * 獲取令牌中所有的聲明將其解析
     * @return 令牌中所有的聲明
     */
    private Claims extractAllClaims(String token) {
        return Jwts
            .parserBuilder()
            .setSigningKey(getSignInKey())
            .build()
            .parseClaimsJws(token)
            .getBody();
    }

    /**
     * 獲取JWT簽名的密鑰
     */
    private Key getSignInKey() {
        byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
        return Keys.hmacShaKeyFor(keyBytes);
    }
}

下一篇要介紹Jwt認證過濾器


上一篇
掌握Spring Security:實現一個堅固的身份驗證與授權架構(EP1)
下一篇
掌握Spring Security:JWT認證過濾器的最佳實踐(EP3)
系列文
Spring Boot & Android Studio教學30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言