JWT (JSON Web Token) là một định dạng nhỏ gọn để truyền dữ liệu giữa các bên dưới dạng đối tượng JSON được bảo vệ bằng chữ ký số. Token có thể được ký bằng HMAC (khóa đối xứng) hoặc RSA/ECDSA (cặp bất đối xứng), đảm bảo tính toàn vẹn và xác thực của dữ liệu. Theo IETF RFC 7519, 2015, JWT được sử dụng trong hàng triệu ứng dụng để xác thực, trao đổi claims an toàn và làm định dạng ID Token trong OpenID Connect.
Những Điểm Chính
JSON Web Token (JWT) là một tiêu chuẩn mở (RFC 7519) định nghĩa cách truyền thông tin nhỏ gọn và tự chứa giữa các bên dưới dạng đối tượng JSON. Thông tin trong JWT được gọi là claims — các tuyên bố về chủ thể (người dùng) và các thuộc tính bổ sung. Mỗi claim là một cặp khóa-giá trị: định danh người dùng, vai trò, thời gian hết hạn, nhà phát hành.
JWT được gọi là tự chứa vì tất cả thông tin cần thiết để xác minh đều nằm trong chính token. Máy chủ không cần truy cập cơ sở dữ liệu hoặc bộ nhớ ngoài để xác minh tính hợp lệ của token — chỉ cần kiểm tra chữ ký. Tính chất này làm cho JWT trở nên lý tưởng cho các hệ thống phân tán và kiến trúc vi dịch vụ, nơi nhiều dịch vụ cần xác thực yêu cầu mà không có kho lưu trữ phiên dùng chung.
Theo Auth0, 2025, hơn 65% ứng dụng di động và web sử dụng JWT làm định dạng token chính cho xác thực API, vượt qua các token mờ và định danh phiên.
JWT bao gồm ba phần được phân cách bằng dấu chấm: header.payload.signature. Mỗi phần là một JSON được mã hóa Base64url. Hãy xem xét từng phần chi tiết.
Header chứa hai trường bắt buộc: alg (algorithm — thuật toán ký) và typ (type — loại token, luôn là “JWT”). Thuật toán có thể đối xứng (HS256 — HMAC với SHA-256) hoặc bất đối xứng (RS256 — RSA với SHA-256, ES256 — ECDSA với P-256). Các thuật toán bất đối xứng được ưa chuộng hơn vì cho phép máy khách xác minh chữ ký mà không cần sở hữu khóa bí mật.
Ví dụ về header đã giải mã:
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-id-1"
}
Payload chứa các claims — tuyên bố về chủ thể. Các claims được chia thành ba loại: đã đăng ký (iss, sub, aud, exp, nbf, iat, jti), công khai (do nhà phát triển định nghĩa trong Sổ đăng ký IANA) và riêng tư (được các bên thỏa thuận). sub (subject) là định danh người dùng duy nhất. exp (expiration) là dấu thời gian hết hạn của token. iss (issuer) là nhà phát hành token.
{
"sub": "user-abc-123",
"iss": "https://auth.example.com",
"aud": "my-mobile-app",
"exp": 1812345678,
"iat": 1812342078,
"role": "premium_user"
}
Signature được tạo bằng cách áp dụng thuật toán ký cho sự kết hợp của header và payload bằng khóa bí mật hoặc khóa riêng. Công thức: HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret) cho HMAC, hoặc RSASHA256(...) cho thuật toán bất đối xứng. Người nhận tính toán chữ ký theo cùng cách và so sánh với chữ ký nhận được — nếu khớp, dữ liệu không bị thay đổi.
Quy trình làm việc với JWT bao gồm hai giai đoạn: tạo (phát hành) token bởi máy chủ xác thực và xác minh token bởi máy khách hoặc máy chủ tài nguyên. Máy chủ xác thực nhận thông tin đăng nhập của người dùng, tạo payload với các claims và ký nó. JWT kết quả được gửi đến máy khách trong phản hồi cho yêu cầu đăng nhập hoặc trong nội dung phản hồi OAuth 2.0 / OpenID Connect.
Trong ứng dụng di động, JWT được sử dụng như sau: sau khi đăng nhập thành công, người dùng nhận được token truy cập ở định dạng JWT. Ứng dụng lưu trữ nó trong bộ nhớ an toàn (Keychain trên iOS, EncryptedSharedPreferences trên Android). Với mỗi yêu cầu API, ứng dụng thêm tiêu đề Authorization: Bearer <token>. Máy chủ API xác minh chữ ký JWT, trích xuất các claims và đưa ra quyết định truy cập dựa trên chúng — mà không cần truy vấn cơ sở dữ liệu.
Theo Google Codelabs, 2025, sử dụng JWT trong Firebase Authentication giảm số lượng yêu cầu đến máy chủ xác thực từ 40–60% so với token phiên, vì dữ liệu được xác minh cục bộ trên mỗi vi dịch vụ. Điều này đặc biệt quan trọng đối với kiến trúc có tải cao, nơi mỗi mili giây độ trễ ảnh hưởng đến trải nghiệm người dùng. Với 50.000 yêu cầu mỗi phút, chuyển sang JWT có thể tiết kiệm tới 10 phiên bản máy chủ xử lý các yêu cầu nội quan.
JWT và Session Token giải quyết cùng một vấn đề — xác thực yêu cầu — nhưng khác nhau cơ bản về kiến trúc. Session Token là một chuỗi định danh ngẫu nhiên tham chiếu đến dữ liệu phiên được lưu trữ trên máy chủ (có trạng thái). JWT là token tự chứa bao gồm tất cả dữ liệu bên trong chính nó (phi trạng thái).
| Tham số | JWT | Session Token |
|---|---|---|
| Lưu trữ dữ liệu | Bên trong token (tự chứa) | Trên máy chủ (lưu trữ phiên) |
| Mở rộng | Không cần lưu trữ dùng chung | Cần Redis/DB cho đa máy chủ |
| Thu hồi token | Phức tạp (cần danh sách đen) | Đơn giản (xóa phiên khỏi DB) |
| Kích thước | Lớn (500–2000 byte) | Nhỏ (16–64 byte) |
| Xác minh chữ ký | Mật mã học | Không có (so sánh chuỗi) |
JWT chiến thắng trong các hệ thống phân tán: các vi dịch vụ có thể xác minh token cục bộ mà không cần kho lưu trữ dùng chung. Ví dụ, trong kiến trúc với năm vi dịch vụ, mỗi dịch vụ xác minh JWT trong 1–2 ms mà không cần gọi mạng, trong khi session token yêu cầu truy vấn Redis tập trung cho mỗi yêu cầu, thêm 10–30 ms độ trễ. Tuy nhiên, JWT khó thu hồi — một khi đã được phát hành, nó có hiệu lực cho đến khi hết hạn. Session Token dễ dàng thu hồi bằng cách xóa bản ghi khỏi DB hoặc Redis.
Đối với ứng dụng di động, cách tiếp cận kết hợp — JWT với thời gian sống ngắn (15–30 phút) và Refresh Token — mang lại sự cân bằng giữa hiệu suất và bảo mật. JWT được sử dụng để truy cập API, trong khi refresh token (thường là mờ) được sử dụng để lấy JWT mới. Nếu JWT bị xâm phạm, kẻ tấn công có quyền truy cập trong 15–30 phút; nếu refresh token bị xâm phạm, phiên sẽ bị chặn thông qua luân chuyển và phát hiện sử dụng lại.
Bảo mật của JWT phụ thuộc vào việc triển khai đúng cách. Lỗ hổng phổ biến nhất là tấn công “alg none”: kẻ tấn công thay đổi header của token thành “alg”: “none”, và máy chủ, không xác minh thuật toán, chấp nhận token giả mạo. Bảo vệ: luôn xác minh rằng thuật toán trong header khớp với thuật toán dự kiến (RS256, ES256) và từ chối các token có alg: none.
Các lỗ hổng JWT cũng bao gồm: khóa bí mật yếu cho HMAC (bị phá trong vài phút), rò rỉ khóa riêng (ký bất kỳ dữ liệu nào thay mặt máy chủ), lưu trữ dữ liệu nhạy cảm trong payload (JWT không mã hóa, chỉ ký), tấn công chèn JWK header (chèn khóa công khai tùy chỉnh). Sử dụng các thư viện đáng tin cậy — Nimbus JOSE + JWT, jjwt (io.jsonwebtoken), PyJWT — làm giảm nguy cơ khai thác các lỗ hổng này.
Một biện pháp bảo mật bổ sung là JWK Thumbprint (RFC 7638): liên kết khóa công khai với token thông qua dấu vân tay (thumbprint) trong header. Nếu máy chủ lưu trữ dấu vân tay dự kiến cho mỗi máy khách, việc chèn JWK header trở nên bất khả thi — máy chủ từ chối bất kỳ khóa nào không khớp với khóa đã đăng ký. OAuth Security Workshop 2025 khuyến nghị JWK Thumbprint như một biện pháp bảo vệ bắt buộc cho tất cả JWT được sử dụng trong các ứng dụng tài chính và y tế.
Thư viện jjwt (auth0/java-jwt) cho phép tạo và xác minh JWT trong ứng dụng Android chỉ trong vài dòng. Trong ví dụ dưới đây, máy chủ tạo token với sub và role, và máy khách xác minh chữ ký. Để lưu trữ an toàn khóa bí mật trên máy chủ, hãy sử dụng biến môi trường hoặc HSM (Mô-đun Bảo mật Phần cứng) — lưu trữ khóa trong mã hoặc tệp cấu hình là một lỗi bảo mật nghiêm trọng.
val secret = "my-256-bit-secret-key-here"
val token = JWT.create()
.withSubject("user-abc-123")
.withIssuer("auth.example.com")
.withClaim("role", "premium_user")
.withExpiresAt(Date(System.currentTimeMillis() + 3600000))
.sign(Algorithm.HMAC256(secret))
// Đang gửi token đến máy khách
println("JWT: $token")
fun verifyToken(token: String): Boolean {
return try {
val decoded = JWT.require(Algorithm.HMAC256(secret))
.withIssuer("auth.example.com")
.build()
.verify(token)
// Chữ ký hợp lệ, các claims đã được trích xuất
println("Subject: ${decoded.subject}")
true
} catch (e: Exception) {
println("Token không hợp lệ: ${e.message}")
false
}
}
Câu Hỏi Thường Gặp
Không. JWT được ký, không được mã hóa — bất kỳ ai cũng có thể giải mã payload Base64 và đọc dữ liệu. Thông tin nhạy cảm (mật khẩu, số thẻ, dữ liệu cá nhân) chỉ được truyền ở dạng mã hóa thông qua JWE (JSON Web Encryption).
ES256 (ECDSA với P-256) được khuyến nghị — nó cung cấp mức bảo mật tương đương RSA 2048-bit với kích thước chữ ký nhỏ hơn đáng kể. RS256 phù hợp cho khả năng tương thích với các hệ thống cũ. HS256 (HMAC) yêu cầu trao đổi khóa bí mật an toàn, điều này khó khăn hơn trong kiến trúc phân tán.
JWT không thể bị thu hồi trực tiếp — nó có hiệu lực cho đến exp. Giải pháp: sử dụng thời gian sống ngắn (15–30 phút), duy trì danh sách đen các jti (JWT ID) đã thu hồi trên máy chủ, hoặc liên kết token với phiên bản khóa bí mật. Refresh token bị thu hồi theo cách tiêu chuẩn — bằng cách xóa khỏi bộ nhớ.
Bearer token là một khái niệm: bất kỳ token nào mà người giữ có thể sử dụng để truy cập. JWT là một định dạng token cụ thể. Bearer token có thể là JWT hoặc chuỗi mờ. JWT thêm tính tự chứa và xác minh mật mã học vào khái niệm Bearer.
Một JWT điển hình với chữ ký RS256 chiếm 500–2000 byte. Nếu payload chứa nhiều claims tùy chỉnh hoặc sử dụng chữ ký bất đối xứng với khóa lớn, kích thước có thể lên tới 4–5 KB. Điều này lớn hơn đáng kể so với session token (16–64 byte), ảnh hưởng đến kích thước tiêu đề HTTP.
Tổng Kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm