Xin chào mọi người, nay mình chia sẻ với mọi người cách sử dụng JWT(Json Web Token) trong Node.js. Để dùng được JWT thì chúng ta phải sử dụng thư viện sau :
$ npm install jsonwebtoken
Thư viện trên giúp ta có thể tạo Token để lưu thông tin data người dùng, khi họ đăng nhập vào chương trình thành công, ta sẽ dùng chuổi Token đó, request truy xuất lấy thông tin người dùng, thường hay dùng trong Restful API xây dựng giữa Client và Server
Bạn có thể tìm hiểu thêm về thư viện này tại đây : https://www.npmjs.com/package/jsonwebtoken
Demo:
Okay, giờ mình tạo một project và cài đặt một số thư viện sau :
npm init npm install express body-parser jsonwebtoken sqlite3 dotenv cors nodemon
Mình sẽ nói sơ qua các thư viện trên như sau :
+ body-parser : Mình cứ hiểu là nó dùng phân tích dữ liệu Data, khi người dùng gửi đến server, bạn có thể tùy chọn phân tích dữ liệu (JSON body parser, Raw body parser, Text body parser, URL-encoded form body parser), bạn có thể xem thêm tại đây : https://www.npmjs.com/package/body-parser
+ jsonwebtoken : Giúp ta tạo mã Token, khi dười dùng Register & Login, dùng mã token đó lấy thông tin ra, kiểm tra thời gian của token, nếu hết hạn thì xử lý nó thế nào tùy bạn
+ sqlite3 : Sử dụng SQLite3 giúp ta tạo database quản lý dữ liệu, truy vấn các câu lệnh query(insert, select,update,delete,....). Bạn có thể xem thêm tại đây : https://www.sqlitetutorial.net/sqlite-nodejs/query/
+ dotenv : Giúp ta tạo file .env quản lý một số key bí mật chương trình
+ cors : Cái này thì nhiều bạn biết, nếu xây server, khi có bắt cứ chỉnh sửa thao tác gì trong code, thì tự động restart lại server, thuận tiện cho việc chỉnh sửa code trong Node.js
Mình chỉ hiểu sao, nói đơn giản là vậy, các bạn có thể tìm hiểu thêm về các thư viện trên, thật sự mình còn chưa hiểu hết về chúng, chỉ sử dụng những cái cần làm thôi, ::)
Đầu tiên mình xây dựng một database lưu thông tin users, bạn hãy lên mạng tìm kiếm và tải phần mềm sqlite về dùng https://sqlitebrowser.org/dl/
CREATE TABLE "users" ( "id" INTEGER, "first_name" TEXT NOT NULL, "last_name" TEXT NOT NULL, "email" TEXT NOT NULL UNIQUE, "password" TEXT NOT NULL, "token" TEXT, PRIMARY KEY("id" AUTOINCREMENT) );
Sau khi tạo xong database nhớ lưu đến project của bạn, để tí ta dùng đến, Okay, bước đầu đã có database rồi, giờ ta đi thẳng vào chương trình luôn.
Hãy tạo file App.js và dán mã bên dưới vào
require("dotenv").config(); const express = require("express"); const jwt = require("jsonwebtoken"); const app = express(); app.use(express.json()); /* DATA SIMBLE */ const sqlite3 = require('sqlite3').verbose(); // Setup the database connection let conn = new sqlite3.Database("./database.db", err => { if (err) { return console.error(err.message); } console.log("Connected to the in-memory SQLite database."); }); app.post("/welcome", (req, res) => { const token = req.body.token || req.query.token || req.headers["x-access-token"]; if (!token) { return res.status(403).send("A token is required for authentication"); } try { var decoded = jwt.verify(token, process.env.TOKEN_KEY); res.status(200).send(decoded.user); } catch (err) { return res.status(401).send("Invalid Token"); } }); // Register app.post("/register", async (req, res) => { // Our register logic starts here try { // Get user input const { first_name, last_name, email, password } = req.body; // Validate user input if (!(email && password && first_name && last_name)) { res.status(400).send("All input is required"); }else{ var sql = "INSERT INTO users(first_name,last_name,email,password) values('" + first_name + "','" + last_name + "','" + email + "','" + password + "')"; conn.serialize(()=>{ conn.run( sql, function (err) { if (err) { res.status(500).json(err.message); }else{ let lastID = this.lastID; res.status(200).json({"success":1,"lastID":lastID}); } } ); }); } /* save user to databse */ } catch (err) { console.log(err); } }); // Login app.post("/login", async (req, res) => { const { email, password } = req.body; if (!(email && password)) { res.status(400).send("All input is required"); } conn.serialize(()=>{ conn.get("select * from users where email=? and password=?", [email, password],async function (err, row) { if(err){ res.status(500).send({'Response':'Error updating user', err }); } if(row){ let _id = row.id; const {first_name,last_name,email} = row; var user ={first_name,last_name,email}; var token = jwt.sign({ exp: Math.floor(Date.now() / 1000) + (60 * 60), user: user }, process.env.TOKEN_KEY); conn.run("update users set token=? where id=?", [token, _id], function (err2) { if (err2) { res.status(400).json({ "description": "Đăng nhập không thành công" }); }else{ res.status(200).json(token); } }); }else{ res.status(500).json({"success":0}); } }); }) }); module.exports = app;
Bạn nhìn đoạn code trên đầu tiên ta cần gọi một số thư viện mà ban đầu ta đã cài đặt
require("dotenv").config(); const express = require("express"); const jwt = require("jsonwebtoken"); const sqlite3 = require('sqlite3').verbose();
Mình có gọi require("dotenv").config(), cho nên bạn cần tạo file .env trong project , lưu các KEY mà bạn cần dùng đến
API_PORT=4001 TOKEN_KEY=webtoken123
Để sử dụng được file .env bạn cần sử dụng câu lệnh process.env.TOKEN_KEY
Tiếp theo ta cần khai báo connect đến databsae trong sqlite, sử dụng câu lên bên dưới
let conn = new sqlite3.Database("./database.db", err => { if (err) { return console.error(err.message); } console.log("Connected to the in-memory SQLite database."); });
Các bạn có thể tìm hiểu về các câu truy vấn Query như (insert, update, select,...) trong SQLite tại đây : https://www.sqlitetutorial.net/sqlite-insert/
Ta có hàm khởi tạo Token trong đoạn code trên, ta cần khai báo thời gian hết hạn của token, để khi hết token, thì ta có thể thông báo cho người dùng biết phiên làm việc đã hết hạn, lúc đó tùy bạn muốn xử lý sao thì tùy
Mình khởi tạo Token có thời gian là 60 phút
var token = jwt.sign({ exp: Math.floor(Date.now() / 1000) + (60 * 60), user: data }, process.env.TOKEN_KEY);
Làm sao để lấy được Token ta dùng đoạn code sau để lấy Token trong Request Header
const token = req.body.token || req.query.token || req.headers["x-access-token"];
Thêm người dùng đăng ký vào database, dưới đây là phương thức run, ta chỉ cần viết câu query vào trong phương thức run , kèm theo tham số là được
db.run(sql, params, function(err){ // }); db.run(`INSERT INTO langs(name) VALUES(?)`, ['C'], function(err) { if (err) { return console.log(err.message); } // get the last insert id console.log(`A row has been inserted with rowid ${this.lastID}`); });
Bạn có thể tìm hiểu thêm một số query trong SQLite tại đây : https://www.sqlitetutorial.net/sqlite-nodejs/query/
Giờ hãy tạo file index.js trong project và viết code khởi chạy server với port gì đó mà bạn đã cung cấp trong file .env
const http = require("http"); const app = require("./app"); const server = http.createServer(app); const { API_PORT } = process.env; const port = process.env.PORT || API_PORT; // server listening server.listen(port, () => { console.log(`Server running on port ${port}`); });
Code trên bạn cần nhớ khai bao file app.js vào nhé , giờ hãy mở file package.json chỉnh lại câu lệnh chạy server , bạn nhìn trong "scripts"
{ "name": "nodejs-react", "version": "1.0.0", "description": "nodejs react", "main": "index.js", "scripts": { "start": "node index.js", "dev": "nodemon index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "nodejs", "react" ], "author": "hoanguyenit", "license": "ISC", "dependencies": { "bcryptjs": "^2.4.3", "body-parser": "^1.19.0", "cors": "^2.8.5", "dotenv": "^10.0.0", "express": "^4.17.1", "jsonwebtoken": "^8.5.1", "mongoose": "^6.0.9", "sqlite3": "^5.0.2", "uid": "^2.0.0" }, "devDependencies": { "nodemon": "^2.0.13" } }
Okay, ta chạy câu lệnh dưới đây khởi động server thôi
npm run dev
Test trong project của chúng ta có các bước sau :
Bước 1: request post("/register") đăng ky thành viên
Bước 2: post("/login") nếu đăng nhập đúng, tạo token lưu thông tin người dùng
Bước 3: post("/welcome"), kèm mã token vào header, để gửi đến server xử lý để lấy thông tin dữ liệu người dùng đã đăng nhập thành công trước đó
Vậy là xong, hẹn bài viết chia sẻ kiến thức tiếp theo ::)