JWT: JSON Web Token là gì, cấu trúc và cách sử dụng

Tác giả: IT Sectr Đã đăng: 2026-04-05 Thời gian đọc: 9 phút

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

  • JWT là token tự chứa, bao gồm tất cả dữ liệu xác minh bên trong chính nó
  • Cấu trúc — ba phần: header, payload và signature, được phân cách bằng dấu chấm
  • Chữ ký — đảm bảo dữ liệu không bị thay đổi sau khi tạo token
  • Phi trạng thái — máy chủ không cần lưu trữ phiên, giúp đơn giản hóa việc mở rộng
  • Bảo mật — JWT không mã hóa dữ liệu, chỉ ký; thông tin nhạy cảm không được đặt trong payload

JWT là gì?

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.

Cấu trúc JWT: header, payload và signature

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 — thuật toán và loại token

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ã:

json
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-id-1"
}

Payload — claims và dữ liệu

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.

json
{
  "sub": "user-abc-123",
  "iss": "https://auth.example.com",
  "aud": "my-mobile-app",
  "exp": 1812345678,
  "iat": 1812342078,
  "role": "premium_user"
}

Signature — xác minh tính toàn vẹn

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.

Cách JWT hoạt động: tạo và xác minh

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.

JWT trong xác thực di động

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 vs Session Token

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ốJWTSession Token
Lưu trữ dữ liệuBên trong token (tự chứa)Trên máy chủ (lưu trữ phiên)
Mở rộngKhông cần lưu trữ dùng chungCần Redis/DB cho đa máy chủ
Thu hồi tokenPhức tạp (cần danh sách đen)Đơn giản (xóa phiên khỏi DB)
Kích thướcLớn (500–2000 byte)Nhỏ (16–64 byte)
Xác minh chữ kýMật mã họcKhông có (so sánh chuỗi)

Ưu điểm và nhược điểm của JWT

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 JWT

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 phổ biến

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ế.

Ví dụ mã: làm việc với JWT trong Kotlin

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.

Tạo JWT

kotlin
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")

Xác minh JWT

kotlin
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

Có thể lưu trữ mật khẩu trong JWT không?

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).

Thuật toán ký JWT nào an toàn nhất?

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.

Làm thế nào để thu hồi JWT trước khi hết hạ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ớ.

JWT khác Bearer token như thế nào?

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.

Kích thước JWT nào được coi là bình thường?

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

  • JWT là token JSON tự chứa nhỏ gọn với chữ ký số
  • Cấu trúc — ba phần: header (thuật toán), payload (claims), signature (chữ ký)
  • Phi trạng thái — máy chủ xác minh token mà không cần truy vấn cơ sở dữ liệu
  • JWT vs Session — JWT thắng về mở rộng, Session thắng về thu hồi
  • Bảo mật — bảo vệ chống alg none, khóa yếu và chèn JWK là bắt buộc
  • Payload không được mã hóa — dữ liệu nhạy cảm cần JWE
  • JWT là định dạng tiêu chuẩn cho ID Token trong OpenID Connect và token Firebase Authentication

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.

Thảo luận dự án

Đọc thêm