JWT: Cấu trúc và Ứng dụng trong Xác thực Web

JSON Web Token (JWT) là tiêu chuẩn mở dựa trên JSON (RFC 7519) dùng để truyền tải thông tin xác thực giữa các hệ thống mạng. Token này được thiết kế gọn nhẹ, an toàn và đặc biệt phù hợp cho các ứng dụng phân tán như SSO. JWT cho phép trao đổi thông tin mà không cần lưu trữ phiên trên máy chủ, giảm thiểu chi phí xử lý.

Quá trình xác thực không trạng thái (stateless) hoạt động như sau:

  • Client gửi thông tin đăng nhập (tên người dùng và mật khẩu) đến server.
  • Server xác minh thông tin và tạo JWT token.
  • Client lưu trữ token và gửi kèm mỗi yêu cầu.
  • Server xác minh token trước khi xử lý yêu cầu.

JWT gồm 3 phần phân tách bằng dấu chấm (.): Header, Payload và Signature. Ví dụ: header.payload.signature.

Header chứa thuật toán ký và loại token:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload chứa các tuyên bố về thực thể, bao gồm claim đã đăng ký, công khai và riêng tư:

{
  "sub": "user_123",
  "role": "admin",
  "iat": 1625097600
}

Signature được tạo bằng cách mã hóa header, payload cùng khóa bí mật và thuật toán đã chỉ định, đảm bảo tính toàn vẹn của token.

Dưới đây là lớp xử lý JWT trong Java sử dụng thư viện JJWT 0.9.x:

@Component
public class JwtService {
    private static final Logger log = LoggerFactory.getLogger(JwtService.class);
    
    private static final String SUBJECT_CLAIM = "sub";
    private static final String ISSUED_AT = "iat";
    @Value("${jwt.secret.key}")
    private String secretKey;
    @Value("${jwt.token.ttl.minutes}")
    private long tokenTtlMinutes;

    private String createToken(Map<String, Object> payload) {
        return Jwts.builder()
                .setClaims(payload)
                .setExpiration(calculateExpiry())
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

    private Claims parseClaims(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            log.error("Invalid token: {}", token);
            return null;
        }
    }

    private Date calculateExpiry() {
        return new Date(System.currentTimeMillis() + tokenTtlMinutes * 60000);
    }

    public String getUsername(String token) {
        Claims claims = parseClaims(token);
        return claims != null ? claims.getSubject() : null;
    }

    public boolean validateToken(String token, UserDetails user) {
        String username = getUsername(token);
        return username != null && username.equals(user.getUsername()) && !isTokenExpired(token);
    }

    private boolean isTokenExpired(String token) {
        Date expiry = getExpiryDate(token);
        return expiry.before(new Date());
    }

    private Date getExpiryDate(String token) {
        Claims claims = parseClaims(token);
        return claims.getExpiration();
    }

    public String generateToken(UserDetails user) {
        Map<String, Object> payload = new HashMap<>();
        payload.put(SUBJECT_CLAIM, user.getUsername());
        payload.put(ISSUED_AT, new Date());
        return createToken(payload);
    }
}

Thẻ: JWT Java spring-boot authentication Security

Đăng vào ngày 28 tháng 6 lúc 22:44