
Chào các "thần đồng" Gen Z! Hôm nay, anh Creyt sẽ "khai sáng" cho các em một khái niệm cực kỳ "hot" và "cool" trong giới lập trình web hiện đại: JWT authentication.
JWT là gì? "Hộ Chiếu Điện Tử" của bạn
Thôi bỏ cái mớ lý thuyết khô khan đi, anh em mình nói chuyện thực tế nhé. Tưởng tượng thế này: Ngày xưa, mỗi lần bạn vào bar xịn, bạn phải xuất trình CCCD/CMND cho bảo vệ. Rồi mỗi lần vào lại, lại phải xuất trình. Mệt không? Quá mệt! Server của chúng ta cũng vậy, mỗi lần bạn gửi yêu cầu lên, nó lại phải hỏi: "Ê, mày là ai? Mày có quyền vào đây không?" và phải đi dò trong cái "sổ đen" (database session) xem bạn đã đăng nhập chưa.
JWT (viết tắt của JSON Web Token) chính là cái "thẻ thành viên VIP" của bạn đó. Lần đầu bạn đăng nhập (như đăng ký thẻ VIP), hệ thống sẽ cấp cho bạn một cái thẻ đặc biệt. Từ đó về sau, mỗi lần bạn muốn vào cửa (gửi request lên server), bạn chỉ cần "quẹt thẻ" này là xong. Bảo vệ (server) chỉ cần nhìn cái thẻ, biết nó hợp lệ, biết bạn là ai và có quyền gì, là cho qua ngay, không cần phải chạy vào phòng lưu trữ lục lọi "sổ đen" nữa.
Cái hay của JWT là gì? Server không cần nhớ mặt bạn, không cần lưu bất kỳ thông tin session nào về bạn. Nó chỉ cần tin vào cái "chữ ký" trên cái thẻ VIP của bạn thôi. "Stateless" là vậy đó, server nhẹ gánh, dễ dàng mở rộng như diều gặp gió.
Cấu tạo của một "Thẻ VIP" JWT
Một cái JWT nhìn thì phức tạp, nhưng thực ra nó chỉ có 3 phần chính, được ngăn cách bởi dấu chấm (.):
-
Header (Phần đầu): Giống như cái bìa của cuốn hộ chiếu, ghi loại tài liệu là gì (JWT) và dùng thuật toán mã hóa nào để bảo vệ nó (ví dụ: HS256 - HMAC SHA256). Nó trông như này khi giải mã base64:
{ "alg": "HS256", "typ": "JWT" } -
Payload (Phần thân): Đây là "nội dung" chính của cái thẻ VIP, chứa các thông tin về bạn (gọi là "claims"). Ví dụ:
user_id,username,rolescủa bạn, và quan trọng nhất làexp(expiration time) - thời gian hết hạn của cái thẻ. Lưu ý cực mạnh: Đừng bao giờ nhét thông tin nhạy cảm như mật khẩu, số thẻ tín dụng vào đây nhé! Payload chỉ là base64 encoded, ai cũng có thể giải mã để đọc được. Nó trông như này:{ "user_id": "12345", "username": "creyt_dev", "role": "admin", "exp": 1678886400 // Thời gian hết hạn (Unix timestamp) } -
Signature (Chữ ký): Đây là phần quan trọng nhất, giống như con dấu niêm phong của chính phủ trên hộ chiếu, đảm bảo cái thẻ VIP này là "hàng thật", không bị làm giả hoặc chỉnh sửa. Nó được tạo ra bằng cách lấy Header + Payload + một "bí mật nhà nghề" (Secret Key) mà chỉ server biết, rồi dùng thuật toán mã hóa. Nếu ai đó cố gắng thay đổi Header hoặc Payload, chữ ký sẽ không khớp, và server sẽ biết ngay đây là hàng "fake".
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Khi ba phần này kết hợp lại, bạn sẽ có một chuỗi dài loằng ngoằng như này:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDUiLCJ1c2VybmFtZSI6ImNyZXl0X2RldiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTY3ODg4NjQwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Code Ví Dụ Minh Hoạ (Node.js): "Triển" ngay thôi!
Giờ thì chúng ta sẽ "bắt tay vào làm" một hệ thống JWT authentication đơn giản với Node.js và Express. Để bắt đầu, bạn cần cài đặt các gói sau:
npm init -y
npm install express jsonwebtoken dotenv
Tiếp theo, tạo file .env để lưu trữ JWT_SECRET của bạn. Đừng bao giờ commit file này lên Git nhé!
JWT_SECRET=daylamotbibatkhongaitheobiet_anhcreyt
Bây giờ là code chính trong server.js:
require('dotenv').config(); // Load biến môi trường từ .env
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json()); // Kích hoạt middleware để đọc JSON từ request body
const JWT_SECRET = process.env.JWT_SECRET;
// Middleware để xác thực JWT
function verifyToken(req, res, next) {
const authHeader = req.headers['authorization'];
// Format của header: 'Bearer TOKEN'
const token = authHeader && authHeader.split(' ')[1];
if (token == null) {
return res.status(401).json({ message: 'Không có token, truy cập bị từ chối' });
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ message: 'Token không hợp lệ hoặc đã hết hạn' });
}
req.user = user; // Gán thông tin người dùng vào request
next(); // Chuyển sang middleware hoặc route tiếp theo
});
}
// 1. Route Đăng nhập (Tạo JWT)
app.post('/login', (req, res) => {
// Đây là phần giả lập việc kiểm tra username/password từ database
const { username, password } = req.body;
if (username === 'creyt' && password === '123456') {
// Nếu đăng nhập thành công, tạo payload cho JWT
const user = {
id: '123',
username: username,
role: 'admin'
};
// Ký JWT với secret key và thời gian hết hạn
const accessToken = jwt.sign(user, JWT_SECRET, { expiresIn: '1h' }); // Hết hạn sau 1 giờ
res.json({ accessToken: accessToken });
} else {
res.status(401).json({ message: 'Sai tên đăng nhập hoặc mật khẩu' });
}
});
// 2. Route được bảo vệ (Chỉ truy cập khi có JWT hợp lệ)
app.get('/profile', verifyToken, (req, res) => {
// req.user đã có thông tin người dùng từ JWT sau khi verifyToken chạy
res.json({
message: `Chào mừng ${req.user.username} đến với trang cá nhân của bạn!`,
user: req.user
});
});
// 3. Một route khác chỉ dành cho admin (ví dụ)
app.get('/admin-dashboard', verifyToken, (req, res) => {
if (req.user.role !== 'admin') {
return res.status(403).json({ message: 'Bạn không có quyền truy cập trang này!' });
}
res.json({ message: `Chào admin ${req.user.username}, đây là bảng điều khiển quản trị!` });
});
app.listen(PORT, () => {
console.log(`Server Creyt đang chạy tại http://localhost:${PORT}`);
});
Cách "test" code này:
-
Đăng nhập: Dùng Postman hoặc Insomnia, gửi request
POSTtớihttp://localhost:3000/loginvới body là JSON:{ "username": "creyt", "password": "123456" }Bạn sẽ nhận được một
accessToken. -
Truy cập route bảo vệ: Gửi request
GETtớihttp://localhost:3000/profile.- Trong phần
Headers, thêmAuthorization:Bearer <accessToken_mà_bạn_nhận_được>. - Thử không gửi
Authorizationheader hoặc gửi token sai, bạn sẽ thấy lỗi401hoặc403.
- Trong phần
Mẹo Vặt (Best Practices) từ "lão làng" Creyt
Để sử dụng JWT "ngon lành cành đào" và an toàn, nhớ vài "mẹo" sau:
- Bảo vệ
JWT_SECRET: Đây là "bí mật nhà nghề" của bạn, là chìa khóa để ký và xác minh token. Giữ nó an toàn như giữ bồ cũ của anh Creyt vậy. Tuyệt đối không để lộ, không commit vào Git. Luôn dùng biến môi trường (nhưprocess.env.JWT_SECRET) và đảm bảo nó đủ mạnh (dài, phức tạp). - Thời hạn token (
exp): Đừng cấp thẻ VIP vĩnh viễn! Token nên có thời gian hết hạn ngắn (vài phút đến vài giờ). Điều này giảm thiểu rủi ro nếu token bị đánh cắp. Khi hết hạn, người dùng phải đăng nhập lại hoặc dùng Refresh Token. - Refresh Tokens: Khi Access Token (cái JWT ngắn hạn) hết hạn, người dùng sẽ cần một token mới. Thay vì bắt họ đăng nhập lại, bạn có thể cấp một Refresh Token (thường là một chuỗi dài, lưu trong database và có thời gian sống dài hơn). Khi Access Token hết hạn, client dùng Refresh Token để yêu cầu một Access Token mới mà không cần nhập lại mật khẩu. Refresh Token nên được lưu trữ an toàn hơn (ví dụ: HttpOnly cookie) và kiểm tra khi sử dụng.
- Không nhét thông tin nhạy cảm vào Payload: Nhắc lại lần nữa, Payload chỉ là Base64 encoded, ai cũng có thể giải mã và đọc được. Chỉ đưa vào những thông tin cần thiết để xác định người dùng và quyền hạn của họ thôi.
- Luôn dùng HTTPS: JWT sẽ được gửi qua mạng trong mỗi request. Nếu không dùng HTTPS, kẻ xấu có thể chặn và đánh cắp token của bạn. HTTPS mã hóa đường truyền, bảo vệ token khỏi bị đọc trộm.
- Lưu trữ token phía client:
- Local Storage: Dễ dùng, nhưng dễ bị tấn công XSS (Cross-Site Scripting) nếu website của bạn có lỗ hổng.
- HttpOnly Cookies: An toàn hơn với XSS vì JavaScript không thể truy cập cookie này. Tuy nhiên, vẫn có thể bị tấn công CSRF (Cross-Site Request Forgery) nếu không có cơ chế bảo vệ phù hợp (như SameSite=Lax/Strict hoặc CSRF tokens riêng).
Ứng dụng thực tế: "Thẻ VIP" này dùng ở đâu?
JWT được sử dụng rộng rãi trong các hệ thống hiện đại, đặc biệt là:
- Single Page Applications (SPAs): Các ứng dụng React, Angular, Vue.js thường dùng JWT để xác thực người dùng sau khi họ đăng nhập.
- Mobile Apps: Các ứng dụng di động như Facebook, Instagram, TikTok thường dùng JWT để giao tiếp với API backend.
- Microservices Architectures: Khi bạn có nhiều dịch vụ nhỏ giao tiếp với nhau, JWT là một cách tuyệt vời để xác thực và ủy quyền giữa các dịch vụ mà không cần một server xác thực trung tâm phức tạp.
- APIs công cộng: Nhiều API cung cấp JWT để các ứng dụng bên thứ ba có thể truy cập tài nguyên của họ.
Khi nào nên dùng và khi nào nên "né"?
Anh Creyt đã từng "thử nghiệm" rất nhiều phương pháp xác thực và rút ra kinh nghiệm xương máu:
Nên dùng JWT khi:
- Bạn cần một hệ thống Stateless: Server không cần lưu trữ session, giúp hệ thống dễ dàng mở rộng theo chiều ngang (scale horizontally).
- Làm việc với Mobile Apps hoặc SPAs: JWT là lựa chọn tự nhiên cho các ứng dụng client-side không có khái niệm session truyền thống.
- Hệ thống Microservices: JWT giúp xác thực người dùng trên nhiều dịch vụ khác nhau một cách hiệu quả.
- Bạn cần giải pháp xác thực đơn giản và hiệu quả: JWT dễ triển khai và hiểu.
Cần cân nhắc hoặc "né" JWT khi:
- Bạn cần khả năng thu hồi token ngay lập tức (Blacklisting): JWT theo bản chất là stateless. Một khi đã ký, token đó vẫn hợp lệ cho đến khi hết hạn. Nếu bạn muốn "đuổi" người dùng ra ngay lập tức (ví dụ: đổi mật khẩu, bị hack), việc thu hồi JWT "đang bay" là khó khăn. Bạn sẽ cần triển khai một cơ chế blacklist riêng (lưu các token đã bị thu hồi vào database hoặc cache) hoặc sử dụng database session truyền thống.
- Bạn cần lưu trữ nhiều thông tin session trên server: Nếu ứng dụng của bạn yêu cầu server phải lưu trữ nhiều trạng thái người dùng (ví dụ: giỏ hàng chưa thanh toán, cài đặt cá nhân tạm thời), thì session-based authentication có thể phù hợp hơn.
Đó, anh Creyt đã "bóc tách" JWT authentication một cách "tận tình" nhất rồi đó. Giờ thì các em đã có thêm một "vũ khí" cực mạnh để "chinh chiến" trong thế giới lập trình web rồi. Nhớ thực hành và áp dụng những mẹo anh Creyt đã chỉ nhé!
Thuộc Series: Nodejs
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!