Contents
JSON Web Token là gì?
JSON Web Token (JWT) là một chuẩn mở (RFC 7519) định nghĩa một cách nhỏ gọn và khép kín để truyền một cách an toàn thông tin giữa các bên dưới dạng đối tượng JSON. Thông tin này có thể được xác minh và đáng tin cậy vì nó có chứa chữ ký số. JWTs có thể được ký bằng một thuật toán bí mật (với thuật toán HMAC) hoặc một public / private key sử dụng mã hoá RSA.
Một ví dụ về JWT Token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJleHAiOjEzODY4OTkxMzEsImlzcyI6ImppcmE6MTU0ODk1OTUiLCJxc2giOiI4MDYzZmY0Y2ExZTQxZGY3YmM5MGM4YWI2ZDBmNjIwN2Q0OTFjZjZkYWQ3YzY2ZWE3OTdiNDYxNGI3MTkyMmU5IiwiaWF0IjoxMzg2ODk4OTUxfQ.
uKqU9dTB6gKwG6jQCuXYAiMNdfNRw98Hw_IWuA5MaMo
Thoạt trông phức tạp là thế nhưng nếu hiểu, cấu trúc của một JWT chỉ đơn giản như sau:
<base64-encoded header>.
<base64-encoded payload>.
<base64-encoded signature>
Nói một cách khác, JWT là sự kết hợp (bởi dấu .) một Object Header dưới định dạng JSON được encode base64, một payload object dưới định dạng JSOn được encode base64 và một Signature cho URI cũng được mã hóa base64 nốt.
Giải thích thêm về 3 thành phần của JWT
Header
Header bao gồm hai phần chính: loại token (mặc định là JWT – Thông tin này cho biết đây là một Token JWT) và thuật toán đã dùng để mã hóa (HMAC SHA256 – HS256 hoặc RSA).
{
"alg": "HS256",
"typ": "JWT"
}
Payload
Payload chứa các claims. Claims là một các biểu thức về một thực thể (chẳng hạn user) và một số metadata phụ trợ. Có 3 loại claims thường gặp trong Payload: reserved, public và private claims.
Reserved claims: Đây là một số metadata được định nghĩa trước, trong đó một số metadata là bắt buộc, số còn lại nên tuân theo để JWT hợp lệ và đầy đủ thông tin: iss (issuer), iat (issued-at time) exp (expiration time), sub (subject), aud (audience), jti (Unique Identifier cho JWT, Can be used to prevent the JWT from being replayed. This is helpful for a one time use token.) … Ví dụ:
{
"iss": "jira:1314039",
"iat": 1300819370,
"exp": 1300819380,
"qsh": "8063ff4ca1e41df7bc90c8ab6d0f6207d491cf6dad7c66ea797b4614b71922e9",
"sub": "batman",
"context": {
"user": {
"userKey": "batman",
"username": "bwayne",
"displayName": "Bruce Wayne"
}
}
}
{
"iss": "scotch.io",
"exp": 1300819380,
"name": "Chris Sevilleja",
"admin": true
}
Public Claims – Claims được cộng đồng công nhận và sử dụng rộng rãi.
Private Claims – Claims tự định nghĩa (không được trùng với Reserved Claims và Public Claims), được tạo ra để chia sẻ thông tin giữa 2 parties đã thỏa thuận và thống nhất trước đó.
Signature
Chữ ký Signature trong JWT là một chuỗi được mã hóa bởi header, payload cùng với một chuỗi bí mật theo nguyên tắc sau:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
Do bản thân Signature đã bao gồm cả header và payload nên Signature có thể dùng để kiểm tra tính toàn vẹn của dữ liệu khi truyền tải.
Xác thực Nodejs với JWT
Cài đặt JWT package
jsonwebtoken là package của Node phát triển dựa trên draft-ietf-jose-json-web-signature-08
Tạo user model
Trong folder api/models folder, tạo một file userModel.js
. Nhưng các bạn đã biết, MongoDB cho phép chúng ta tạo một schema và là nơi chúng ta có thể tạo documents. Chúng ta sẽ sử dụng nó để tạo user trực tiếp trong User document. Trong bài viết này chúng ta sử dụng package mongoose
của nodejs để tạo schema với các thuộc tính như sau:
- fullName
- email address
- passwork
- created (ngày tạo)
'use strict';
var mongoose = require('mongoose'),
bcrypt = require('bcrypt'),
Schema = mongoose.Schema;
//User Schema
var UserSchema = new Schema({
fullName: {
type: String,
trim: true,
required: true
},
email: {
type: String,
unique: true,
lowercase: true,
trim: true,
required: true
},
hash_password: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
}
});
UserSchema.methods.comparePassword = function(password) {
return bcrypt.compareSync(password, this.hash_password);
};
mongoose.model('User', UserSchema);
Tạo phương thức cho user (sign in, register and yêu cầu login)
Trong thư mục api/controllers tạo file userController.js
Trong file userController
chúng ta tạo export 3 thương thức khác nhau với NodeJS
'use strict';
var mongoose = require('mongoose'),
jwt = require('jsonwebtoken'),
bcrypt = require('bcrypt'),
User = mongoose.model('User');
exports.register = function(req, res) {
};
exports.sign_in = function(req, res) {
};
exports.loginRequired = function(req, res, next) {
};
Trong phương thức đăng kí, chúng ta tạo instance user model với User Schema và lưu trong MongoDB
exports.register = function(req, res) {
var newUser = new User(req.body);
newUser.hash_password = bcrypt.hashSync(req.body.password, 10);
newUser.save(function(err, user) {
if (err) {
return res.status(400).send({
message: err
});
} else {
user.hash_password = undefined;
return res.json(user);
}
});
};
Lưu ý: Password lưu xuống database phải bcrypt bằng cách sử dụng Bcrypt package của nodejs
Trong phương thức sign_in chúng ta thực hiện việc hoạt động login. Đầu tiên, chúng ta kiểm tra user đó có tồn tại trong database hay không thông qua email(primary). Nếu có chúng ta kiểm tra password mới phương thức comparePassword
trong user model user. Nếu thành công response json với tham số email, fullName, id (những tham số này đã được mã hóa khi truyền đến clients). Nếu không match thì response trả lỗi về client với NodeJS
exports.sign_in = function(req, res) {
User.findOne({
email: req.body.email
}, function(err, user) {
if (err) throw err;
if (!user) {
res.status(401).json({ message: 'Authentication failed. User not found.' });
} else if (user) {
if (!user.comparePassword(req.body.password)) {
res.status(401).json({ message: 'Authentication failed. Wrong password.' });
} else {
return res.json({token: jwt.sign({ email: user.email, fullName: user.fullName, _id: user._id}, 'RESTFULAPIs')});
}
}
});
};
Trong phương thúc loginRequired
middleware kiểm tra user đã login hay là chưa. Phương thức này sẽ chạy đầu tiên thành công next()
sẽ chuyển hướng đến các hoạt động tiếp theo
exports.loginRequired = function(req, res, next) {
if (req.user) {
next();
} else {
return res.status(401).json({ message: 'Unauthorized user!' });
}
};
Tạo user routes NodeJS
Trong folder api/routes tạo file todoListRoutes.js
. Đây là router cho user register, signup vào sign in để trong cập trên ứng dụng web của chúng ta
'use strict';
module.exports = function(app) {
var userHandlers = require('../controllers/userController.js');
app.route('/auth/register')
.post(userHandlers.register);
app.route('/auth/sign_in')
.post(userHandlers.sign_in);
};
Updating server.js file Nodejs
Chúng ta tạo file server.js
. File này chúng ta tạo instanse User và thiết lập middleware cho express server để check trạng thái của user.
'use strict';
var express = require('express'),
app = express(),
port = process.env.PORT || 3000,
mongoose = require('mongoose'),
Task = require('./api/models/todoListModel'),
User = require('./api/models/userModel'),
bodyParser = require('body-parser'),
jsonwebtoken = require("jsonwebtoken");
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/Tododb');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
Sau đó chúng ta tạo middleware cho express, phải chắc chắn rằng middleware này chạy đầu tiên
app.use(function(req, res, next) {
if (req.headers && req.headers.authorization && req.headers.authorization.split(' ')[0] === 'JWT') {
jsonwebtoken.verify(req.headers.authorization.split(' ')[1], 'RESTFULAPIs', function(err, decode) {
if (err) req.user = undefined;
req.user = decode;
next();
});
} else {
req.user = undefined;
next();
}
});
Như vậy chúng ta đã tạo server, model, controller đã xong. Bây giờ chúng ta init server và kết hợp với postman để test API chúng ta viết ra
npm run start
Đây là những thứ tự hình ảnh mà chúng ta test với postman


Kết luận
Như vậy chúng ta đã hiểu về JWT là gì ? Và cách sử dụng JWT kết hợp với NodeJS. Mong bạn có thể áp dụng vào project, app của bản thân.
Tìm hiểu thêm về xác thực NodeJS : Authentication trong Nodejs là gì ? Tại sao quan trọng ?

Bài viết này được sưu tầm và tổng hợp từ nhiều nguồn trên Internet.
Nếu có gì không hiểu thì inbox messenger bên dưới mình hỗ trợ thêm nhé.