Giải mã một JWT Token trong Java

Người dịch: Nguyễn Thành Trung - Học viên lớp Java08
Email liên hệ: nguyenthanhtrung.nlk58@gmail.com
Bài viết gốc: https://www.baeldung.com/java-jwt-token-decode

1. Tổng quát

JSON Web Token (JWT) thường được sử dụng trong bảo mật API REST. Mặc dù token có thể được phân tích cú pháp bởi các framework như Spring Security OAuth, chúng ta có thể xử lý token bằng code của riêng mình.

Trong hướng dẫn này, chúng ta sẽ giải mã và xác minh tính toàn vẹn của JWT.

2. Cấu trúc của một JWT

Trước tiên, hãy hiểu cấu trúc của JWT:

  • header
  • payload (thường được gọi là nội dung)
  • signature

Signature là tùy chọn. JWT hợp lệ có thể chỉ bao gồm phần header và payload. Tuy nhiên, chúng ta sử dụng phần Signature để xác minh nội dung của header và payload để ủy quyền bảo mật .

Các phần được biểu diễn dưới dạng các chuỗi được mã hóa base64url được phân tách bằng dấu phân cách dấu chấm (‘.’). Theo thiết kế, bất kỳ ai cũng có thể giải mã JWT và đọc nội dung của phần header và payload. Nhưng chúng ta cần quyền truy cập vào khóa bí mật được sử dụng để tạo signature nhằm xác minh tính toàn vẹn của Token.

Thông thường nhất, JWT chứa “yêu cầu” của người dùng. Những dữ liệu này đại diện cho dữ liệu về người dùng mà API có thể sử dụng để cấp quyền hoặc theo dõi người dùng cung cấp mã thông báo. Giải mã mã thông báo cho phép ứng dụng sử dụng dữ liệu và xác thực cho phép ứng dụng tin tưởng rằng JWT được tạo bởi một nguồn đáng tin cậy.

3. Giải mã JWT

Chúng ta có thể giải mã Token bằng các hàm Java tích hợp sẵn.

Trước tiên, hãy chia Token thành các phần của nó:

 String[] chunks = token.split("\\.");

Chúng ta nên lưu ý rằng biểu thức chính quy được chuyển đến String.split sử dụng một escaped ‘.’ character to avoid ‘.’ nghĩa là “any character.”

Mảng của chúng ta bây giờ sẽ có hai hoặc ba phần tử tương ứng với các phần của JWT.

Tiếp theo, hãy giải mã phần header và phần payload bằng bộ giải mã base64url:

Base64.Decoder decoder = Base64.getUrlDecoder();

String header = new String(decoder.decode(chunks[0]));
String payload = new String(decoder.decode(chunks[1]));

Hãy chạy code này với JWT (chúng ta có thể giải mã trực tuyến để so sánh kết quả):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkJhZWxkdW5nIFVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.qH7Zj_m3kY69kxhaQXTa-ivIpytKXXjZc1ZSmapZnGE

Đầu ra sẽ cung cấp cho chúng ta header được giải mã bất kỳ payload nào:

{"alg":"HS256","typ":"JWT"}{"sub":"1234567890","name":"Baeldung User","iat":1516239022}

Nếu chỉ phần header và phần payload được xác định trong JWT, chúng ta đã hoàn thành và giải mã thông tin thành công.

4. Xác minh JWT

Tiếp theo, chúng ta có thể xác minh tính toàn vẹn của header và payload để đảm bảo rằng chúng không bị thay đổi bằng cách sử dụng phần signature.

4.1. Dependencies

Để xác minh, chúng ta có thể thêm jjwt vào pom.xml của mình :

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

Chúng ta cần lưu ý rằng chúng ta cần một phiên bản của thư viện này từ phiên bản 0.7.0 trở đi.

4.2. Cấu hình thuật toán signature và Key Specification

Để bắt đầu xác minh payload và header, chúng ta cần cả thuật toán signature được sử dụng ban đầu để ký Token và secretKey:

SignatureAlgorithm sa = HS256;
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), sa.getJcaName());

Trong ví dụ này, chúng ta đã mã hóa thuật toán signature của mình thành HS256 . Tuy nhiên, chúng ta có thể giải mã JSON của header và đọc trường alg để lấy giá trị này.

Chúng ta cũng cần lưu ý rằng biến secretKey là một biểu diễn chuỗi của secretKey. Chúng ta có thể cung cấp điều này cho ứng dụng của mình thông qua cấu hình của nó hoặc thông qua API REST được cung cấp bởi dịch vụ phát hành JWT.

4.3. Thực hiện xác minh

Bây giờ chúng ta đã có thuật toán signature và secretKey, chúng ta có thể bắt đầu thực hiện xác minh.

Hãy kết hợp lại header và payload thành một JWT không dấu, kết hợp chúng với dấu ‘.’ dấu phân cách:

String tokenWithoutSignature = chunks[0] + "." + chunks[1];
String signature = chunks[2];

Bây giờ chúng ta có token chưa được ký và signature được cung cấp. Chúng ta có thể sử dụng thư viện để xác thực nó:

DefaultJwtSignatureValidator validator = new DefaultJwtSignatureValidator(sa, secretKeySpec);

if (!validator.isValid(tokenWithoutSignature, signature)) {
    throw new Exception("Could not verify JWT token integrity!");
}

Hãy phá bỏ điều này.

Đầu tiên, chúng ta tạo trình xác thực với thuật toán và secret đã chọn. Sau đó, chúng ta cung cấp cho nó dữ liệu token chưa được ký và signature được cung cấp.

Sau đó, trình xác thực tạo ra một signature mới và so sánh nó với signature đã cung cấp. Nếu chúng bằng nhau, chúng tôi đã xác minh tính toàn vẹn của header và payoad.

5. Kết luận

Trong bài viết này, chúng ta đã xem xét cấu trúc của JWT và cách giải mã nó thành JSON.

Sau đó, chúng ta sử dụng một thư viện để xác minh tính toàn vẹn của token bằng cách sử dụng signature, thuật toán và secretKey của nó.