Opaque Token là gì? So sánh Opaque Token và JWT
30 Jun, 2025
Hướng nội
AuthorOpaque Token là chuỗi ký tự không thể giải mã hay trích xuất thông tin từ phía client, đóng vai trò tham chiếu đến trạng thái xác thực lưu trữ trên server

Mục Lục
Nếu như JWT đã trở thành lựa chọn quen thuộc đối với nhiều lập trình viên khi xây dựng các hệ thống xác thực, thì Opaque Token lại nổi bật với những ưu điểm riêng, đặc biệt về mặt bảo mật thông tin. Trong bài viết này, chúng ta sẽ cùng tìm hiểu chi tiết về Opaque Token, so sánh trực tiếp với JWT và khám phá cách triển khai hệ thống xác thực sử dụng Opaque Token với Node.js và TypeScript.
1. JWT (JSON Web Token)
Trước khi đi sâu vào Opaque Token, chúng ta hãy cùng điểm qua một chút về JWT.
JWT (JSON Web Token) là một loại token tự chứa thông tin (self-contained), bao gồm ba phần chính: Header, Payload và Signature, được phân tách bởi dấu chấm (.). Nhờ cấu trúc này, JWT có thể mang theo dữ liệu xác thực, giúp server giảm tải việc tra cứu dữ liệu mỗi lần xác minh token.
xxxxx.yyyyy.zzzzz
- Header: Chứa thông tin về thuật toán mã hóa.
- Payload: Chứa dữ liệu (claims) về người dùng (ví dụ:
userId
,roles
,email
). Dữ liệu này chỉ được mã hóa Base64, không phải mã hóa bảo mật, nên bất kỳ ai cũng có thể giải mã và đọc được. - Signature: Chữ ký điện tử để đảm bảo token không bị thay đổi trên đường truyền.
Điểm mấu chốt của JWT là stateless. Server không cần lưu trữ bất kỳ thông tin gì về token sau khi đã phát hành. Khi nhận lại token từ client, server chỉ cần xác thực chữ ký là có thể tin tưởng vào dữ liệu trong payload.
2. Opaque Token là gì?
Khác với JWT, Opaque Token (hay còn gọi là Reference Token) chỉ đơn giản là một chuỗi ký tự ngẫu nhiên, không chứa bất kỳ thông tin nào về người dùng hoặc các claims liên quan nếu chỉ nhìn vào nó.
Ví dụ về một Opaque Token: v2.local.adsjSDA723b1-asdnsa...
Token này chỉ đóng vai trò như một "mã tham chiếu". Khi client gửi token này lên máy chủ, server sẽ phải thực hiện thao tác tra cứu: dùng chuỗi token làm khóa để tìm kiếm thông tin session tương ứng trong cơ sở dữ liệu (có thể là Redis, PostgreSQL hoặc một hệ thống lưu trữ khác).
Quy trình hoạt động tổng quát của Opaque Token như sau:
- Client gửi thông tin đăng nhập (username, password) tới Authentication Server.
- Server xác thực thông tin. Nếu hợp lệ, server sẽ tạo một phiên làm việc (session) chứa thông tin người dùng và lưu vào database.
- Server sinh ra một chuỗi token ngẫu nhiên, duy nhất, rồi liên kết token này với session vừa được tạo.
- Server trả Opaque Token lại cho client.
- Client lưu token này và gửi kèm trong header
Authorization: Bearer <token>
cho những lần request xác thực tiếp theo. - Khi nhận request, Resource Server (API Server) lấy token từ header và gửi truy vấn tới Authentication Server hoặc trực tiếp tra cứu trong database để xác thực token.
- Nếu token còn hiệu lực và hợp lệ, server sẽ truy xuất được thông tin người dùng liên quan để tiến hành xử lý yêu cầu.
3. So sánh Opaque Token và JWT
Việc lựa chọn giữa Opaque Token và JWT phụ thuộc rất nhiều vào yêu cầu của dự án.
Tiêu chí | Opaque Token | JWT (JSON Web Token) |
---|---|---|
Cấu trúc | Chuỗi ký tự ngẫu nhiên, không có ý nghĩa. | Cấu trúc header.payload.signature chứa dữ liệu (claims). |
Trạng thái | Stateful. Server phải lưu trữ thông tin về token. | Stateless. Server không cần lưu trữ gì, mọi thông tin đã có trong token. |
Bảo mật | Cao hơn. Vì token không chứa dữ liệu, nếu bị lộ cũng vô dụng. Dễ dàng thu hồi (revoke) ngay lập tức bằng cách xóa khỏi DB. | Thấp hơn. Payload có thể bị giải mã và đọc. Rất khó để thu hồi token trước khi nó hết hạn. |
Hiệu năng | Chậm hơn. Mỗi lần xác thực token đòi hỏi một lượt truy vấn database, gây ra độ trễ. | Nhanh hơn. Xác thực chỉ cần tính toán chữ ký bằng CPU, không cần I/O. |
Kích thước | Nhỏ gọn. Thường là một chuỗi có độ dài cố định. | Lớn hơn. Kích thước tăng theo số lượng claims trong payload. |
Khả năng mở rộng | Phức tạp hơn trong microservices. Các service cần phải có cách để gọi đến một nguồn xác thực trung tâm (centralized auth service/database). | Dễ mở rộng hơn. Bất kỳ service nào có secret key đều có thể tự xác thực token mà không cần phụ thuộc vào bên ngoài. |
Khi nào nên sử dụng Opaque Token?
- Khi bảo mật là yếu tố hàng đầu: Bạn cần khả năng thu hồi token ngay lập tức trong các trường hợp như người dùng đổi mật khẩu, đăng xuất khỏi tất cả thiết bị, hoặc tài khoản bị khóa.
- Phù hợp với ứng dụng nội bộ (first-party apps): Chẳng hạn, các website truyền thống hoặc ứng dụng di động do chính công ty bạn phát triển, nơi việc xác thực thông qua một API nội bộ không gây ảnh hưởng lớn đến hiệu năng.
- Không tiết lộ thông tin người dùng trong token: Opaque Token hoàn toàn không chứa dữ liệu nhạy cảm, đảm bảo rằng thông tin người dùng không bị lộ ra ngoài.
Khi nào nên sử dụng JWT?
- Khi hiệu năng là ưu tiên: Với những hệ thống có lượng request lớn, JWT giúp loại bỏ các bước tra cứu database, giảm tối đa độ trễ và nâng cao hiệu năng tổng thể.
- Kiến trúc Microservices: JWT cho phép mỗi service tự xác thực request mà không cần phụ thuộc vào một điểm kiểm tra tập trung, giúp các microservice hoạt động độc lập và linh hoạt hơn.
- Xác thực cho ứng dụng bên thứ ba (third-party apps): JWT phù hợp để tích hợp xác thực với các ứng dụng bên ngoài, cho phép các bên thứ ba xác thực người dùng một cách an toàn và hiệu quả.
4. Hướng dẫn triển khai Opaque Token với Node.js, Express, và TypeScript
Trong ví dụ này, chúng ta sẽ triển khai cơ chế xác thực sử dụng Opaque Token, với Redis làm nơi lưu trữ session nhờ tốc độ truy xuất vượt trội.
Yêu cầu:
- Đã cài đặt Node.js trên máy.
- Có một instance Redis đang chạy
4.1 Khởi tạo dự án và cài đặt thư viện
Cấu trúc thư mục:
opaque-token-example/
├── src/
│ ├── authMiddleware.ts
│ └── server.ts
├── package.json
└── tsconfig.json
mkdir opaque-token-example
cd opaque-token-example
npm init -y
npm install express redis uuid
npm install -D typescript @types/express @types/node @types/uuid ts-node-dev
Tạo file tsconfig.json
:
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
4.2 Viết mã nguồn
src/server.ts
: Đây là file chính, định nghĩa các endpoints cho việc đăng nhập, đăng xuất và truy cập tài nguyên được bảo vệ.
import express, { Request, Response, NextFunction } from 'express';
import { createClient } from 'redis';
import { v4 as uuidv4 } from 'uuid';
import { authMiddleware } from './authMiddleware';
const app = express();
app.use(express.json());
// Khởi tạo Redis client
export const redisClient = createClient({
// url: 'redis://your-redis-instance' // Cấu hình nếu cần
});
redisClient.on('error', (err) => console.log('Redis Client Error', err));
// Dữ liệu người dùng giả lập
const users = [{
id: 'user-1',
username: 'testdev',
password: 'password123'
}];
// Endpoint: Đăng nhập
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
return res.status(401).json({ message: 'Invalid credentials' });
}
// 1. Tạo Opaque Token ngẫu nhiên
const token = uuidv4();
// 2. Lưu thông tin session vào Redis với token làm key
// Session sẽ hết hạn sau 1 giờ (3600 giây)
await redisClient.set(token, JSON.stringify({ userId: user.id }), {
EX: 3600
});
// 3. Trả token về cho client
return res.json({ accessToken: token });
});
// Endpoint: Lấy thông tin cá nhân (được bảo vệ)
// Chúng ta sẽ sử dụng middleware xác thực ở đây
app.get('/profile', authMiddleware, (req: Request, res: Response) => {
// Nhờ có middleware, chúng ta có thể truy cập thông tin user qua req.user
res.json({ message: 'Đây là thông tin profile của bạn', user: req.user });
});
// Endpoint: Đăng xuất
app.post('/logout', authMiddleware, async (req, res) => {
// Để đăng xuất, chỉ cần xóa token khỏi Redis
const token = req.headers.authorization?.split(' ')[1];
if (token) {
await redisClient.del(token);
}
res.status(200).json({ message: 'Logged out successfully' });
});
const startServer = async () => {
await redisClient.connect();
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
};
startServer();
src/authMiddleware.ts
: Middleware này sẽ chặn các request, kiểm tra token và xác thực nó với Redis.
import { Request, Response, NextFunction } from 'express';
import { redisClient } from './server';
// Mở rộng kiểu Request của Express để chứa thông tin user
declare global {
namespace Express {
interface Request {
user?: any;
}
}
}
export const authMiddleware = async (req: Request, res: Response, next: NextFunction) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ message: 'Authorization token required' });
}
const token = authHeader.split(' ')[1];
try {
// Tra cứu token trong Redis
const sessionData = await redisClient.get(token);
if (!sessionData) {
// Nếu không tìm thấy -> token không hợp lệ hoặc đã hết hạn
return res.status(401).json({ message: 'Invalid or expired token' });
}
// Gắn thông tin user vào request để các handler sau có thể sử dụng
req.user = JSON.parse(sessionData);
next();
} catch (error) {
console.error('Authentication error:', error);
return res.status(500).json({ message: 'Internal server error' });
}
};
4.3 Chạy ứng dụng
Thêm script vào package.json
:
"scripts": {
"start": "ts-node-dev --respawn --transpile-only src/server.ts"
}
Bây giờ, chạy lệnh:
npm start
5. Kết luận
Không có giải pháp xác thực nào phù hợp cho tất cả mọi trường hợp. Cả Opaque Token và JWT đều là những công cụ hiệu quả, mỗi loại có những ưu thế và hạn chế riêng.
Là một lập trình viên, việc nắm vững nguyên lý hoạt động của cả hai phương pháp này sẽ giúp bạn đưa ra quyết định đúng đắn, đáp ứng các yêu cầu kiến trúc và bảo mật của từng dự án. Hy vọng bài viết đã giúp bạn hiểu rõ hơn về Opaque Token cũng như cách áp dụng thực tế trong Node.js và TypeScript.