I. Giới thiệu về JWT
JSON Web Token (JWT) là một tiêu chuẩn mở (RFC 7519) định nghĩa cách thức truyền tin an toàn giữa các bên bằng JSON object. Thông tin token được xác thực và đánh dấu dựa trên Signature của nó.
Phần Signature này được mã hoá bằng HMAC SHA-256 hoặc RSA.
Ví dụ mẫu về token của JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ. BSgTbdPIxfESPo8jmkxqWRBwfwp-8BFWqwg9cnO2kxk |
Các bạn có thể lên trang chủ của JSON Web Token để thấy rõ hơn.
Hình 1 : Thành phần của chuỗi JWT
Mục đích chính của JWT để xác thực user và xác thực hành vi của user đó. Cụ thể về mục đích và tại sao nên sử dụng JWT sẽ nói rõ trong phần sau.
II. Các thành phần của JWT
Các thành phần của JWT
Hình 2: Cấu trúc của JWT
Khi các bạn search trên mạng từ khoá JWT thì sẽ thấy hình trên khá là quen, bởi vì nó mô tả đầy đủ cấu trúc của JWT.
JSON Web Token bao gồm 3 phần, được ngăn cách nhau bởi dấu chấm (.):
- Header
- Payload
- Signature (chữ ký)
Tổng quát thì nó có dạng như sau:
Header.Payload.Signature |
- Header
Phần header bao gồm 2 phần:
- "typ": "JWT" => Khai báo rằng loại token là JWT
- "alg": "HS256" => Signature của nó sử dụng thuật toán mã hóa HMAC SHA-256, ở đây có thể thay là RSA nếu chúng ta muốn dùng thuật toán mã hoá là RSA
Sau đó đoạn header này sẽ được mã hoá Base64Url. Kết quả ta được phần đầu tiên của JWT là:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 |
- Payload
Trong payload sẽ chứa các khai báo (claim). Các claim này sẽ bao gồm dữ liệu mà ta muốn truyền đi, và có thể cả các thông tin khác về token của ta nữa. Chia nhỏ ra thì claim bao gồm các loại như sau:
a. Registered (Reserved) claims
Đây là một tập hợp các Claims được định nghĩa sẳn bởi JWT tuy không bắt buộc nhưng được khuyến khích sử dụng, vì các claims này chứa các thông tin về token như là ngày hết hạn, ngày phát hành hay tổ chức tạo token, …
- iss (issuer): tổ chức phát hành token
- sub (subject): chủ đề của token
- aud (audience): đối tượng sử dụng token
- exp (expired time): thời điểm token sẽ hết hạn
- nbf (not before time): token sẽ chưa hợp lệ trước thời điểm này
- iat (issued at): thời điểm token được phát hành, tính theo UNIX time
- jti: JWT ID
Những claims này đã được quy định ở trong IANA JSON Web Token Claims registry, các bạn có thể tham khảo chi tiết tại Registered Claim Names :
b. Public claims
Các claim này do ta tùy ý định nghĩa để truyền tải thông tin mà mình muốn. Tuy nhiên, để cho dễ hiểu và tránh tình trạng conflict, các claim dạng này nên được khai báo tại IANA JSON Web Token Registry hoặc là 1 URI có chứa không gian tên không bị trùng lặp.
c. Private claims
Các claims này do chúng ta tự định nghĩa để chứa những thông tin mà ta muốn truyền đi (không được trùng với Reserved Claims và Public Claims). Thông thường trong ứng dụng thì chúng ta hay dùng loại này để chứa dữ liệu mà chúng ta cần như là user ID, để khi decode token ra thì biết token này của user gữi lên.
Bên trên là tổng quan về phần thứ 2 (Payload) của JWT. Decode thữ đoạn thứ 2 của một token của account đã login vào web chat realtime thì được như hình 3.
Hình 3: Decode thữ phần payload của JWT
Trong payload chứa các thông tin như:
Private claims: _id, username.
Registered (Reserved) claims: iat, exp
Các bạn có thể tham khảo hoặc lấy get một cái token thực tế qua ứng dụng chat realtime. Đăng ký tài khoản và đăng nhập thành công, sau đó vào Inspect => Application => Local Storage => URL thì sẽ thấy được token của ứng dụng này được lưu vào local storage của browser.
Hình 4: JWT lưu trữ trong local storage
- Signature
Phần chữ ký được tạo bằng cách kết hợp 2 phần Header + Payload đã encode bằng Base64Url, rồi mã hóa nó với secrect key với thuật toán được chỉ định rõ ở phần header.
Như nãy là thuật toán được sử dụng là HS256 thì signature sẽ được tạo ra như sau:
Với cách thực hiện này thì signature được sử dụng để xác minh message không bị thay đổi trên đường đi và trong trường hợp token được ký bằng secret key, nó cũng có thể xác minh rằng người gửi token là ai.
Thì đó là phần thứ 3 (Signature) của JWT.
Tổng quát lại thì JWT có 3 dạng như hình, các bạn có thể lên trang của JWT tham khảo thêm
Hình 5: Cấu trúc của JWT trong trang chủ jwt
III. Khi nào nên sử dụng JWT
Authorization: Đây là ứng dụng phổ biến nhất để sử dụng JWT. Khi người dùng đã đăng nhập, mỗi yêu cầu tiếp theo sẽ bao gồm JWT. Điều này cho phép người dùng được cấp quyền truy cập vào các url, service, và resource mà mã Token đó cho phép.
Information Exchange: JSON Web Token là 1 cách thức khá hay để truyền thông tin an toàn giữa các thành viên với nhau, nhờ vào phần signature của nó. Phía người nhận có thể biết được người gửi là ai thông qua phần signature. Và chữ ký được tạo ra bằng việc kết hợp cả phần header, payload lại nên thông qua đó ta có thể xác nhận được chữ ký có bị giả mạo hay không.
IV. Cách thức hoạt động của JWT
Ví dụ cụ thể trong ứng dụng chat realtime khi nãy thì trong phần login khi xác thực sử dụng JWT để Authenticate user. Khi user đăng nhập thành công (Browser sẽ post username và password về Server), Server sẽ trả về “accessToken” và “refreshToken” là các chuỗi JWT về Browser, và Token JWT này cần được lưu lại trong Browser của người dùng (thường là LocalStorage hoặc Cookies), thay vì cách truyền thống là tạo một session trên Server và trả về Cookie.
Bất cứ khi nào mà User muốn thực hiện các hành vi nào mà chỉ có User đã đăng nhập mới được phép thì Client sẽ gửi request kèm theo token JWT (để đâu tuỳ theo người code). Ở đây trong hình để ở trong phần Request Headers mục x-access-token:
Hình 6: Chèn token vào header.
Sơ đồ mô tả quá trình này như Hình 7:
Hình 7: Sở đồ mô tả quá trình hoạt động của JWT
accessToken là token JWT mà cái này dùng để chứng thực user và hành vi của user đó. Còn refreshToken dùng để gữi lên xin accessToken mới cho user. Với cách làm này thì khi accessToken của user khi gần hết hạn thì sẽ tự động xin lại và user sẽ không cần phải đăng nhập lại, điều này duy trì sự đăng nhập của user.
V. Ví dụ thực tế về ứng dụng của JWT
Jwt là một cách thức xác thực user nên chúng ta có thể hoàn toàn tự code ra được. Nhưng jwt hỗ trợ thư viện hầu hết các ngôn ngữ nên chúng ta có thể import vào sử dụng một cách sẽ dàng. Dưới đây là code ví dụ sử dụng JWT trong ứng dụng chat realtime đề cập ở trên.
Hình 8: Source code mô tả phần login trong ứng dụng chat realtime
Luồng thực thi của phần login có sử dụng JWT:
- Kiểm tra các tham số của req xem đã đủ chưa như là username, password. Nếu chưa đủ thì return fail
- Nếu đủ thì tìm user đó trong mongodb, nếu không có thì return fail, nếu có thì kiểm tra password, nếu sai thì return fail
- Nếu đúng password thì tiến hành tạo payload (các thông tin mình cần gữi, payload này là phần thứ 2 trong cấu trúc của jwt, trong lúc encode phần này thì JWT tự động thêm các trường được quy định sẳn như exp, iat, … sau đó mới Base64url Encoding) để tạo accessToken(3 days) và refreshToken(30 days) (số thời gian tuỳ vào coder qui định) bằng hàm createToken(hàm này sẽ show code ở hình dưới)
- Sau đó gữi về cho frontend lưu vào local storage hoặc một chổ nào đó trên browser.
Hình 9: Source code hàm createToken
Để ý hàm createToken thì hàm này dùng để tạo token với tham số truyền vào là thời gian hết hạn token. Hàm sign để tạo token của thư viện jsonwebtoken.
jwt.sign(user, config.secrect, { expiresIn: expire}); |
Các tham số:
- user: là payload tạo ở hàm trên
- secrect: là secrect key được sử dụng để mã hoá loại HMAC-SHA256
- expiresIn: là thời gian hết hạn.
Hình 10: Verify JWT trong middleware
Khi user thực hiện các hành vi hay sử dụng service nào cần có sự xác thực thì sẽ đi qua middleware. Middleware này dùng để chèn giữa các request gữi lên server dùng để kiểm tra token của user đó có hợp lệ hay không, nếu không có token gữi lên hay token không hợp lệ thì return fail.
Hàm jwt.verify dùng để decode token đó với secret key lúc nãy, sau khi decode xong thì kết quả sẽ trả về trong hàm callback, lúc đó result(decoded) chưa phần payload (thông tin chúng ta truyền vào khi tạo token). Chúng ta có thể lấy các thông tin này xác thực user nào đã gọi lên service.
Như vậy bài viết đã nói lên tổng quát về JWT, các cấu trúc của nó và cách sử dụng. Hi vọng giúp các bạn phần nào hiểu được các thức xác thực khá hay này.
Bài viết - Hai Nguyen
Tài liệu tham khảo:
[1] JWT Introduction
[2] RFC7519
[3] Chat realtime app source code