Expiresin jwt là gì
10 tháng 07, 2016 - 7226 lượt xem Show Thời gian qua, mình có phải vật lộn với 1 framework mới, mày mò trong tài liệu hướng dẫn thì thấy nó có đề cập tới 1 phương pháp xác thực quyền truy cập (Authentication) bằng JSON Web Token (JWT). Sau khi đào sâu hơn về cái này, mình nhận thấy quả thực JWT nó còn tuyệt vời hơn ngoài mong đời và khả năng của nó sẽ không chỉ dừng lại ở mỗi Authentication. Qua bài viết này mình muốn giúp những ai còn chưa biết tới JWT hoặc chưa hiểu rõ về nó hình dung được mô tả trực quan nhất về JWT và những gì mà JWT có thể đem lại cho Web Service. JSON Web Token là gì?JSON Web Token (JWT) là 1 tiêu chuẩn mở (RFC 7519) định nghĩa cách thức truyền tin an toàn giữa các thành viên bằng 1 đối tượng JSON. Thông tin này có thể được xác thực và đánh dấu tin cậy nhờ vào "chữ ký" của nó. Phần chữ ký của JWT sẽ được mã hóa lại bằng HMAC hoặc RSA. Ví dụ cho 1 JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJpc3MiOiJ0b3B0YWwuY29tIiwiZXhwIjoxNDI2NDIwODAwLCJodHRwOi8vdG9wdGFsLmNvbS9qd3RfY2xhaW1zL2lzX2FkbWluIjp0cnVlLCJjb21wYW55IjoiVG9wdGFsIiwiYXdlc29tZSI6dHJ1ZX0. yRQYnWzskCZUxPwaQupWkiUzKELZ49eM7oWxAQK_ZXwNhững đặc điểm nổi bật của JWT:
Khi nào nên dùng JSON Web Token?Dưới đây là 1 vài kịch bản thích hợp với JWT:
Cấu trúc của JSON Web Token:JSON Web Token bao gồm 3 phần, được ngăn cách nhau bởi dấu chấm (.):
Tổng quát thì nó có dạng như sau: xxxxx.yyyyy.zzzzzHãy cùng nhau khám phá mỗi phần bên trong JWT nhé: Header:Phần Header dùng để khai báo kiểu chữ ký và thuật toán mã hóa sẽ dùng cho cái token của chúng ta. Ví dụ cho phần Header: { “alg”: “HS256”, “typ”: “JWT” }Đoạn Header này khai báo rằng đối tượng được mã hóa là 1 JWT (để phân biệt với JWS hay JWE), và chữ ký của nó sử dụng thuật toán mã hóa HMAC SHA-256. Đoạn Header này sẽ được mã hóa base64url, và ta thu được phần đầu tiên của JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Chú ý rằng mình viết ở phía trên là base64url chứ không phải là base64. Về cơ bản 2 cái này là tương tự nhau nhưng giữa chúng vẫn có những sự khác biệt:
Các bạn có thể so sánh sự khác biệt của chúng ở trang web encode online này: Chúng ta có thể tự triển khai 1 hàm encode base64url do chính mình tạo ra. Dưới đây là code mô phỏng bằng Javascript: function base64url(source) { // Encode in classical base64 encodedSource = CryptoJS.enc.Base64.stringify(source); // Remove padding equal characters encodedSource = encodedSource.replace(/=+$/, ''); // Replace characters according to base64url specifications encodedSource = encodedSource.replace(/\+/g, '-'); encodedSource = encodedSource.replace(/\//g, '_'); return encodedSource; }Ở đoạn code trên mình đã sử dụng thư viện CryptoJS để có thể mã hóa base64 rồi sau đó loại bỏ các ký tự = và thay thế các ký tự + / đi. Để có thể sử dụng được hàm trên, đầu vào của bạn cần là 1 mảng byte ở định dạng UTF-8. Ta có thể chuyển đổi từ xâu ký tự sang mảng byte bằng 1 hàm khác cũng được cung cấp bởi CryptoJS: var source = "Hello!"; // 48 65 6c 6c 6f 21 console.log(CryptoJS.enc.Utf8.parse(source).toString());Cuối cùng ta đã thu được phần đầu tiên của JWT: var header = { "alg": "HS256", "typ": "JWT" }; var stringifiedHeader = CryptoJS.enc.Utf8.parse(JSON.stringify(header)); var encodedHeader = base64url(stringifiedHeader);Payload (Claims):Phần thứ 2 của token đó là Payload, nơi chứa các nội dung của thông tin (claim). Thông tin truyền đi có thể là mô tả của 1 thực thể (ví dụ như người dùng) hoặc cũng có thể là các thông tin bổ sung thêm cho phần Header. Nhìn chung, chúng được chia làm 3 loại: reserved, public và private.
Ví dụ cho phần Payload: { “iss”: “techmaster”, “exp”: 1426420800, “https://www.techmaster.vn/jwt_claims/is_admin”: true, “user”: “paduvi”, “awesome”: true }Mã hóa base64url ta thu được phần thứ 2 của token: eyJpc3MiOiJ0ZWNobWFzdGVyIiwiZXhwIjoxNDI2NDIwODAwLCJodHRwczovL3d3dy50ZWNobWFzdGVyLnZuL2p3dF9jbGFpbXMvaXNfYWRtaW4iOnRydWUsInVzZXIiOiJwYWR1dmkiLCJhd2Vzb21lIjp0cnVlfQSignature:Phần chữ ký được tạo bằng cách kết hợp 2 phần Header + Payload, rồi mã hóa nó lại bằng 1 giải thuật encode nào đó, càng phức tạp thì càng tốt, ví dụ như HMAC SHA-256 $encodedContent = base64UrlEncode(header) + “.” + base64UrlEncode(payload); $signature = hashHmacSHA256($encodedContent);Rồi ta sẽ thu được phần cuối của token: uL7nEjM7ihbQe7l01rmQCtGYoKyb4VyabWqX8PZKdt4Putting All Together:Tổng kết lại, JWT gom lại từ ví dụ trên sẽ có dạng là: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZWNobWFzdGVyIiwiZXhwIjoxNDI2NDIwODAwLCJodHRwczovL3d3dy50ZWNobWFzdGVyLnZuL2p3dF9jbGFpbXMvaXNfYWRtaW4iOnRydWUsInVzZXIiOiJwYWR1dmkiLCJhd2Vzb21lIjp0cnVlfQ.uL7nEjM7ihbQe7l01rmQCtGYoKyb4VyabWqX8PZKdt4Và đây là đoạn code Javascript triển khai toàn bộ công việc trên: Mình chỉ minh họa như vậy thôi, chứ không khuyến khích mọi người tự mất công làm lại tất cả các công đoạn vì hiện nay đã có rất nhiều thư viện hỗ trợ công việc này. Các bạn có thể tham khảo danh sách các thư viện và thử debug JWT ở trên trang web https://jwt.io/. Hiện tại mình đang lập trình Node.js và Golang nên đề xuất 2 thư viện rất dễ sử dụng, đó là: jsonwebtoken (Node.js) và dgrijalva/jwt-go (Golang) Ví dụ đơn giản về Authentication bằng JWTCode bên phía API Server, sử dụng Golang: package main import ( "github.com/dgrijalva/jwt-go" jwtmiddleware "github.com/iris-contrib/middleware/jwt" "github.com/kataras/iris" ) func main() { myJwtMiddleware := jwtmiddleware.New(jwtmiddleware.Config{ ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { return []byte("My Secret"), nil }, SigningMethod: jwt.SigningMethodHS256, }) iris.Get("/secured/ping", myJwtMiddleware.Serve, SecuredPingHandler) iris.Listen(":8080") } type Response struct { Text string `json:"text"` } func SecuredPingHandler(ctx *iris.Context) { response := Response{"All good. You only get this message if you're authenticated"} // get the *jwt.Token which contains user information using: // user:= myJwtMiddleware.Get(ctx) or context.Get("jwt").(*jwt.Token) ctx.JSON(iris.StatusOK, response) }Bây giờ nếu mình vào thử đường dẫn http://localhost:8080/secured/ping bằng trình duyệt ta sẽ thu được kết quả là: Required authorization token not foundĐúng như dự kiến, truy cập vào đường dẫn không thành công do mình chưa khai báo token. Để có thể authenticate thành công, ta cần bổ sung thêm HTTP Header cho request phía client truy cập. Phía dưới là code minh họa bằng Node.js: /** * Created by phanducviet on 7/11/16. */ const url = 'http://localhost:8080/secured/ping', request = require('request'), jwt = require('jsonwebtoken'), payload = { user: 'paduvi', company: 'Techmaster' }, secretKey = 'My Secret'; var token = jwt.sign(payload, secretKey, {algorithm: 'HS256', expiresIn: '1h'}); var callback = function (error, response, body) { if (error) { console.error(error); } else { console.log("Status Code: " + response.statusCode); console.log("Response Data: " + body); } } var options = { url: url, headers: { 'Authorization': 'Bearer ' + token } } request(options, callback); // or request.get(url, { 'auth': { 'bearer': token } }, callback);Kết quả hiển thị trên console là: Status Code: 200 Response Data: {"text":"All good. You only get this message if you're authenticated"}Nếu như mình dùng secret key không hợp lệ, kết quả trả về sẽ là: Status Code: 401 Response Data: signature is invalidHay bổ sung thêm Reserved Claim nbf (Not before time) với thời gian là khoảng vài phút sau thì kết quả là: Status Code: 401 Response Data: Token is not valid yetNgoài ra, các bạn có thể chỉnh sửa lại code để test nốt với các trường hợp còn lại có thể xảy ra, ví dụ như exp (expired at), iat (issued at)... |