Authentication Nodejs with JWT
Bài đăng này đã không được cập nhật trong 3 năm
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.
Trên đây là định nghĩa cơ bản về JWT, thực ra có nhiều bài viết nói về JWT nên trong bài viết này mình sẽ ko đi chi tiết , mà chỉ trình bày cách sử dụng nó để tạo Authentication trong ứng dụng đơn gian theo các step. Trong bài viết này mình sẽ sử dụng node js, mongodb, express....Và cách sử dụng các tools này thì mình sẽ ko đề cập mà mặc định là đã biết sư dụng
Tạo cấu trúc ban đầu
Chạy câu lênh sau ở thư mục lưu project
npm install express-generator -g
Install jwt
npm install jsonwebtoken
và install các dependencies khác như sau:
{
"name": "AppName",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"bcrypt": "^1.0.3",
"body-parser": "~1.17.1",
"cookie-parser": "~1.4.3",
"debug": "~2.6.3",
"express": "~4.15.2",
"jade": "~1.11.0",
"jsonwebtoken": "^7.4.3",
"mongoose": "^4.10.8",
"morgan": "~1.8.1",
"serve-favicon": "~2.4.2"
}
}
Câu trúc thư mục cơ bản như sau:
Tạo user model
Trong thư mục models tạo file user.js Model user sẽ lưu thông tin của 1 account như username, password, email,... Password của user sẽ đc mã hoá và compare bằng cách sử dụng modul bcrypt user.js
//Require Mongoose
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
//Define a schema
var UserSchema = new mongoose.Schema({
username:{ type: String, unique: true, required: true },
hash_password: { type: String, required: true},
email: { type: String , lowercase: true, trim: true},
created_date: String,
first_name: String,
last_name: String,
birth_date: String,
gender: String,
avatar: String
}, {
collection: 'user'
});
// comparePassword
UserSchema.methods.comparePassword = function (password) {
return bcrypt.compareSync(password, this.hash_password)
}
//Export function to create "UserSchema" model class
module.exports = mongoose.model('User', UserSchema );
Trong thư mục Controller, tạo usercontroller. Trong controller này sẽ có 2 function như sau: usercontroller.js
var User = require('../models/user');
var config = require('../helpers/config');
var jwt = require('jsonwebtoken');
var bcrypt = require('bcrypt');
// register
exports.register = function(req, res) {
// get user
var user = new User(req.body);
user.hash_password = bcrypt.hashSync(req.body.password, 10);
// save
user.save(function (err, newUser) {
if (err) {
res.json({"code": false, "message": "Error to save"});
return
}
newUser.hash_password = undefined
res.json({ message: 'Save ok', data: newUser });
});
};
// login
exports.login = function(req, res) {
var username = req.body.username;
var password = req.body.password;
// find
User.findOne({
'username': username
}, function(err, user) {
if (!user) {
res.json({ error : 'User is not exist'})
} else if (user &&
user.comparePassword(password)) {
var payload = { username: user.username };
var jwtToken = jwt.sign(payload, config.jwtSecret, { expiresIn: 1 * 30 });
console.log('jwtToken: ' + jwtToken);
var jsonResponse = {'access_token': jwtToken, 'refresh_token': "xxxxx-xxx-xx-x"}
res.json(jsonResponse)
} else {
res.json({ error : 'Login Error'})
}
})
};
Tạo route user (user.js) trong thư mục routes user.js
var express = require('express');
var router = express.Router();
var user_controller = require('../controllers/userController');
var auth_controller = require('../controllers/authController');
// Register
router.post('/register', user_controller.register);
// Login
router.post('/login', user_controller.login);
// Logout
router.get('/logout', auth_controller.isAuthenticated, user_controller.logout);
module.exports = router;
- Register: Lấy thông tin từ req và save vào database (Mongodb).Ở đây password sẽ được mã hoá bằng cách sử dụng bcrypt.hashSync(req.body.password, 10);
- Login : Api này lấy thông tin login từ user (username, password).Nếu login thành công sẽ tạo jwtToken bằng cách sử dụng module "jsonwebtoken" .jwtToken sẽ được trả về cho user
authController
Trong controller này sẽ có hàm check xem api đã được chứng thực hay chưa authController.js
var User = require('../models/user');
var config = require('../helpers/config');
var jwt = require('jsonwebtoken');
var bcrypt = require('bcrypt');
exports.isAuthenticated = function(req, res, next) {
if (req.headers &&
req.headers.authorization &&
req.headers.authorization.split(' ')[0] === 'JWT') {
var jwtToken = req.headers.authorization.split(' ')[1];
jwt.verify(jwtToken, config.jwtSecret, function(err, payload) {
if (err) {
res.status(401).json({message: 'Unauthorized user!'});
} else {
console.log('decoder: ' + payload.username);
// find
User.findOne({
'username': payload.username
}, function(err, user) {
if (user) {
req.user = user;
next();
} else {
res.status(401).json({ message: 'Unauthorized user!' });
}
})
}
});
} else {
res.status(401).json({ message: 'Unauthorized user!' });
}
};
Tất cả api đòi hỏi quyền access đều phải set header "authorization" = "JWT {jwttoken}".Và function này sẽ check và trả về 401 nếu api ko đc cấp quyền. Api nào cần check quyền sẽ được set trên route như ví dụ sau. users.js
var express = require('express');
var router = express.Router();
var users_controller = require('../controllers/usersController');
var auth_controller = require('../controllers/authController');
// GET list users
router.get('/', auth_controller.isAuthenticated, users_controller.users);
module.exports = router;
Đây là route users.js. Ở đây khi muốn lấy thông tin của tất cả user trên server, api phải được chứng thực. Đo đó, auth_controller.isAuthenticated sẽ check nếu được chứng thực thì sẽ gọi tiếp đến users_controller.users, còn không sẽ respone code 401 :'Unauthorized user!'
usersController.js
var User = require('../models/user');
// get current user
exports.users = function(req, res) {
// find
User.find({
}, function(err, users) {
if (err)
throw err;
if (users) {
var jsonResponse = {'users': users}
res.json(jsonResponse)
} else {
res.send(JSON.stringify({
error : 'Login Error'
}))
}
})
};
Others
dbController.js
var mongoose = require('mongoose');
var config = require('../helpers/config');
exports.connectMongoDb = function() {
//connect to MongoDB
var mongodbUri = config.dbUri
var options = {
server: { socketOptions: { keepAlive: 300000, connectTimeoutMS: 30000 } },
replset: { socketOptions: { keepAlive: 300000, connectTimeoutMS: 30000 } }
};
mongoose.connect(mongodbUri, options);
};
configs.js
module.exports = {
'jwtSecret': 'taodeptrai',
'dbUri': 'mongodb://admin:xxxxxxxxx@dsxxxxx.mlab.com:xxxxx/appname'
};
Test
Register
curl -X POST \
http://localhost:3000/api/v1/user/register \
-H 'cache-control: no-cache' \
-H 'content-type: application/x-www-form-urlencoded' \
-H 'postman-token: 74debd36-ba43-a6b7-9e9e-4f16d4f4864d' \
-d 'username=ltranframgia5&password=12345678'
Login
curl -X POST \
http://localhost:3000/api/v1/user/login \
-H 'cache-control: no-cache' \
-H 'content-type: application/x-www-form-urlencoded' \
-H 'postman-token: 7df0cb78-70ee-e786-a07c-7ebe3805e34d' \
-d 'username=ltranframgia5&password=12345678'
Get Users
curl -X GET \
http://localhost:3000/api/v1/users \
-H 'authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imx0cmFuZnJhbWdpYTUiLCJpYXQiOjE1MDM5MTQ0OTMsImV4cCI6MTUwMzkxNDUyM30.VnTxYSX70HJmj7N7IDD_LRb3FicbkedRZ9CN_8MIBTI' \
-H 'cache-control: no-cache' \
-H 'postman-token: 216139be-2710-2747-c9c3-8ab3f3d86a30'
All rights reserved