JWT – JSON Web Token
JWT (JSON Web Token) là một phương thức tiêu chuẩn dùng để truyền tải thông tin an toàn giữa Client và Server dưới dạng một đối tượng JSON. Thông tin này có thể được xác thực và tin cậy vì nó chứa một chữ ký số (Digital Signature). Trong Node.js, JWT được sử dụng phổ […]
JWT (JSON Web Token) là một phương thức tiêu chuẩn dùng để truyền tải thông tin an toàn giữa Client và Server dưới dạng một đối tượng JSON. Thông tin này có thể được xác thực và tin cậy vì nó chứa một chữ ký số (Digital Signature).
Trong Node.js, JWT được sử dụng phổ biến nhất để xử lý Xác thực (Authentication) và Cấp quyền (Authorization) trong các kiến trúc RESTful API thay thế cho cơ chế Session truyền thống.
Cấu trúc của một chuỗi JWT
Một chuỗi JWT hoàn chỉnh được phân tách bằng dấu chấm . thành 3 phần riêng biệt:
- Header.Payload.SignatureHeader (Phần đầu): Chứa kiểu token (mặc định là JWT) và thuật toán mã hóa chữ ký (thường là HS256).
- Payload (Thành phần chính): Chứa dữ liệu bạn muốn lưu trữ (ví dụ: userId, role, email). Lưu ý: Phần này chỉ được mã hóa dạng Base64, bất kỳ ai cũng có thể giải mã để đọc được. Do đó, tuyệt đối không lưu thông tin nhạy cảm như mật khẩu tại đây.
- Signature (Chữ ký số): Được tạo ra bằng cách gộp Header + Payload và ký bằng một Chuỗi bí mật (Secret Key) duy nhất nằm trên Server. Phần này giúp Server phát hiện xem dữ liệu trong Payload có bị Client cố tình sửa đổi hay không.
Trọn bộ code triển khai JWT trong Node.js (Express)
Để bắt đầu, bạn cần cài đặt thư viện:
npm install jsonwebtoken
Bước 1: Tạo (Ký) mã Token khi Đăng nhập thành công
Sử dụng hàm jwt.sign() để tạo ra một chuỗi mã hóa gửi về cho Client.
const jwt = require('jsonwebtoken');
const loginController = async (req, res) => {
// Giả định bạn đã kiểm tra tài khoản/mật khẩu trong database thành công
const userFromDB = { id: '64f8b2c1', email: 'user@gmail.com', role: 'admin' };
// 1. Định nghĩa dữ liệu nạp vào Token (Payload)
const payload = {
userId: userFromDB.id,
role: userFromDB.role
};
// 2. Định nghĩa chuỗi bí mật bảo mật cao (Thường lưu trong file .env)
const SECRET_KEY = 'chuoi_bi_mat_sieu_cap_khong_duoc_lo';
// 3. Tiến hành ký và tạo Token
const token = jwt.sign(payload, SECRET_KEY, {
expiresIn: '1h' // Thời gian sống của token: 1 giờ (Ví dụ: '10m', '7d')
});
// 4. Trả token về cho client lưu trữ
res.json({
message: 'Đăng nhập thành công!',
accessToken: token
});
};
Bước 2: Tạo Middleware xác thực Token (Verify JWT) cho các Route bảo mật
Mỗi khi Client gửi yêu cầu vào các trang cần quyền đăng nhập, họ phải gửi đính kèm token này vào Header Authorization. Server sẽ dùng jwt.verify() để kiểm tra độ tính thực của nó.
const jwt = require('jsonwebtoken');
const verifyTokenMiddleware = (req, res, next) => {
// 1. Lấy chuỗi cấu trúc "Bearer " từ Header của Request
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Tách lấy chuỗi token đứng sau chữ Bearer
// Nếu không tìm thấy token gửi lên
if (!token) {
return res.status(401).json({ message: 'Từ chối truy cập! Bạn chưa đăng nhập.' });
}
try {
const SECRET_KEY = 'chuoi_bi_mat_sieu_cap_khong_duoc_lo';
// 2. Tiến hành giải mã và xác thực chữ ký
const decoded = jwt.verify(token, SECRET_KEY);
// 3. Đính kèm dữ liệu đã giải mã vào req để Route Handler phía sau sử dụng
req.user = decoded;
next(); // Hợp lệ, cho phép đi tiếp qua chốt kiểm soát!
} catch (error) {
// Trả về lỗi 403 nếu token đã hết hạn hoặc bị chỉnh sửa chữ ký trái phép
return res.status(403).json({ message: 'Mã token đã hết hạn hoặc không hợp lệ!' });
}
};
Bước 3: Áp dụng vào hệ thống Routes
const express = require('express');
const app = express();
// Route công khai không cần token
app.post('/api/login', loginController);
// Route bảo mật, bắt buộc đi qua middleware verifyTokenMiddleware
app.get('/api/vip-news', verifyTokenMiddleware, (req, res) => {
// Nhờ có middleware giải mã trước đó, bạn có thể lấy dữ liệu ra dễ dàng
const { userId, role } = req.user;
res.json({
message: 'Chào mừng bạn đến với chuyên mục VIP!',
yourInfo: `ID của bạn là ${userId} và quyền của bạn là ${role}`
});
});
Ưu và nhược điểm của JWT so với Session truyền thống
| Tiêu chí | Session truyền thống | JWT (JSON Web Token) |
| Nơi lưu dữ liệu | Server lưu trong RAM/Database. Client giữ ID [shopping]. | Lưu hoàn toàn trong chuỗi Token nằm tại máy Client. |
| Khả năng mở rộng (Scalability) | Kém. Chạy nhiều Server song song phải đồng bộ hóa Session bằng Redis [shopping]. | Rất tốt. Server không cần lưu gì, chỉ cần dùng thuật toán để giải mã, phù hợp hệ thống Microservices. |
| Hủy trạng thái (Revoke) | Dễ dàng. Server chỉ cần xóa bản ghi Session là người dùng lập tức bị đăng xuất. | Khó. Token đã phát ra thì Server không thể thu hồi từ xa, nó sẽ có hiệu lực cho đến khi tự hết hạn (Ví dụ sau 1h). |
Kiến thức nâng cao cho dự án thực tế: Khắc phục nhược điểm của JWT
Vì bản chất JWT không thể thu hồi từ xa, nếu một token của User bị hacker lấy trộm, hacker sẽ có quyền truy cập suốt 1 giờ đồng hồ mà bạn không thể ngăn chặn.
Giải pháp: Sử dụng cơ chế Cặp bài trùng Access Token & Refresh Token.
- Access Token: Cấp thời gian sống cực ngắn (Ví dụ: 15 phút). Dùng để gọi API hằng ngày.
- Refresh Token: Cấp thời gian sống rất dài (Ví dụ: 30 ngày). Token này được lưu an toàn trong database của server.
Khi Access Token hết hạn, Client tự động âm thầm gửi Refresh Token lên một endpoint riêng để xin Server cấp lại một mã Access Token mới mà người dùng không hề hay biết. Nếu phát hiện rủi ro, bạn chỉ cần xóa Refresh Token trong Database là tài khoản đó sẽ lập tức bị vô hiệu hóa khi Access Token cũ hết hiệu lực.