0
0
Lập trình
Admin Team
Admin Teamtechmely

JWT là gì? Cách hoạt động của Khóa Bí mật và Khóa Công khai

Đăng vào 7 tháng trước

• 8 phút đọc

Giới thiệu

JSON Web Token (JWT) là một phương pháp an toàn để truyền tải thông tin giữa các bên. Nó thường được sử dụng trong xác thực API, nơi mà thông tin người dùng được mã hóa và gửi đi dưới dạng token. Trong bài viết này, chúng ta sẽ tìm hiểu hai phương pháp xác thực JWT: Khóa Bí mật + JWT (phương pháp đối xứng) và Khóa Công khai + Khóa Riêng (phương pháp bất đối xứng). Chúng ta cũng sẽ xem xét các thực tiễn tốt nhất, những cạm bẫy phổ biến và một số mẹo hiệu suất hữu ích.

📋 Mục lục


Tổng quan

JWT là gì?

JWT là một chuỗi ký tự được mã hóa, bao gồm ba phần:

  • Header: Chứa thông tin về thuật toán mã hóa và loại token.
  • Payload: Chứa các thông tin (claims) về người dùng.
  • Signature: Dùng để xác thực token.

Luồng xác thực

Cả hai phương pháp đều tuân theo luồng cơ bản sau:

  1. Khách hàng nhận một token JWT.
  2. Khách hàng gửi token trong các yêu cầu API.
  3. Máy chủ xác thực token và xử lý yêu cầu.

Phương pháp 1: Khóa Bí mật + JWT

🔍 Khi nào sử dụng

  • Môi trường ứng dụng đơn lẻ.
  • Tạo token tập trung (cùng máy chủ tạo và xác thực).
  • Tình huống triển khai đơn giản.
  • API nội bộ trong cùng một tổ chức.

🛠️ Thiết lập

Bước 1: Tạo Khóa Bí mật

javascript Copy
// Tạo một khóa bí mật an toàn
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"

Bước 2: Cấu hình Môi trường

plaintext Copy
# .env file
JWT_SECRET=khóa_bí_mật_64_ký_tự_của_bạn
JWT_EXPIRES_IN=24h
JWT_ALGORITHM=HS256

Bước 3: Cấu hình Máy chủ

javascript Copy
// authService.js (phương pháp Khóa Bí mật)
import jwt from 'jsonwebtoken';

const JWT_SECRET = process.env.JWT_SECRET;
const JWT_ALGORITHM = 'HS256';

class AuthService {
  // Tạo token
  generateToken(user) {
    return jwt.sign(
      { 
        sub: user.id,
        username: user.username,
        iat: Math.floor(Date.now() / 1000)
      },
      JWT_SECRET,
      { 
        expiresIn: process.env.JWT_EXPIRES_IN || '24h',
        algorithm: JWT_ALGORITHM 
      }
    );
  }

  // Xác thực token
  verifyToken(token) {
    return jwt.verify(token, JWT_SECRET, { algorithms: [JWT_ALGORITHM] });
  }
}

🔐 Ví dụ về Tạo Token

javascript Copy
// Ví dụ: Tạo một token JWT (phía máy chủ)
const authService = new AuthService();

const user = {
  id: "user123",
  username: "john_doe"
};

const token = authService.generateToken(user);
console.log("Token đã tạo:", token);

📡 Gửi Yêu cầu API

bash Copy
# Sử dụng curl
curl -X POST http://localhost:8000/translate \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"text": "Xin chào thế giới"}'

Phương pháp 2: Khóa Công khai + Khóa Riêng

🔍 Khi nào sử dụng (Triển khai hiện tại)

  • Hệ thống phân tán (microservices).
  • Tích hợp bên thứ ba.
  • Yêu cầu an ninh cao hơn.
  • Tách biệt việc tạo token và xác thực.
  • Nhiều nhà phát hành token.

🛠️ Thiết lập

Bước 1: Tạo Cặp Khóa RSA

bash Copy
# Sử dụng trình tạo khóa đã cung cấp
npm run generate-keys

# Hoặc thủ công với OpenSSL
openssl genrsa -out jwt-private.key 2048
openssl rsa -in jwt-private.key -pubout -out jwt-public.key

Bước 2: Cấu hình Môi trường

plaintext Copy
# .env file
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"
JWT_ALGORITHM=RS256

Bước 3: Cấu hình Máy chủ (Hiện tại)

javascript Copy
// authService.js (phương pháp Khóa Công khai)
import jwt from 'jsonwebtoken';

const JWT_PUBLIC_KEY = process.env.JWT_PUBLIC_KEY;
const JWT_ALGORITHM = process.env.JWT_ALGORITHM || 'RS256';

class AuthService {
  constructor() {
    this.jwtPublicKey = this.loadPublicKey();
  }

  loadPublicKey() {
    if (process.env.JWT_PUBLIC_KEY) {
      return process.env.JWT_PUBLIC_KEY.replace(/\\n/g, '\n');
    }
    console.warn('⚠️ Cảnh báo: Không tìm thấy khóa công khai JWT!');
    return null;
  }

  // Chỉ xác thực token (không tạo trên máy chủ)
  verifyToken(token) {
    return new Promise((resolve, reject) => {
      jwt.verify(token, this.jwtPublicKey, { algorithms: [this.jwtAlgorithm] }, (err, decoded) => {
        if (err) reject(err);
        else resolve(decoded);
      });
    });
  }
}

🔐 Tạo Token (Bên ngoài)

Lưu ý: Với phương pháp khóa công khai/khóa riêng, các token được tạo bên ngoài bằng cách sử dụng khóa riêng.

Sử dụng Node.js

javascript Copy
// external-token-generator.js
import jwt from 'jsonwebtoken';
import fs from 'fs';

const privateKey = fs.readFileSync('./jwt-private.key', 'utf8');

function generateToken(user) {
  const payload = {
    sub: user.id,
    username: user.username,
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + (24 * 60 * 60) // 24 giờ
  };

  return jwt.sign(payload, privateKey, { algorithm: 'RS256' });
}

// Ví dụ sử dụng
const user = { id: "user123", username: "john_doe" };
const token = generateToken(user);
console.log("Token đã tạo:", token);

Sử dụng Python

python Copy
# external_token_generator.py
import jwt
import time
from cryptography.hazmat.primitives import serialization

# Tải khóa riêng
with open('./jwt-private.key', 'rb') as f:
    private_key = serialization.load_pem_private_key(
        f.read(),
        password=None
    )

def generate_token(user_id, username):
    payload = {
        'sub': user_id,
        'username': username,
        'iat': int(time.time()),
        'exp': int(time.time()) + (24 * 60 * 60)  # 24 giờ
    }

    return jwt.encode(payload, private_key, algorithm='RS256')

# Ví dụ sử dụng
token = generate_token("user123", "john_doe")
print(f"Token đã tạo: {token}")

📡 Gửi Yêu cầu API

bash Copy
# Kiểm tra trạng thái xác thực
curl http://localhost:8000/auth/status

# Xác thực token của bạn
curl -X GET http://localhost:8000/auth/verify \
  -H "Authorization: Bearer <token>"

# Thực hiện yêu cầu dịch thuật
curl -X POST http://localhost:8000/translate \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"text": "Xin chào thế giới"}'

🧪 Kiểm tra

bash Copy
# Chạy bộ kiểm tra với token của bạn
JWT_TOKEN="token_của_bạn" npm run test-auth

# Kiểm tra client
JWT_TOKEN="token_của_bạn" node jwt-client.js

So sánh

Tính năng Khóa Bí mật + JWT Khóa Công khai + Khóa Riêng
An ninh Đối xứng (bí mật chung) Bất đối xứng (khóa riêng)
Tạo Token Phía máy chủ Bên ngoài/Phía client
Quản lý Khóa Một khóa bí mật Khóa công khai trên máy chủ, khóa riêng bảo mật
Khả năng mở rộng Giới hạn ở một dịch vụ Thân thiện với hệ thống phân tán
Độ phức tạp Đơn giản Phức tạp hơn
Trường hợp sử dụng API nội bộ Tích hợp bên thứ ba
Thuật toán HS256 RS256

Các cân nhắc về an ninh

Phương pháp Khóa Bí mật

  • ✅ Thực hiện đơn giản
  • ✅ Xác thực nhanh
  • ❌ Rủi ro bí mật chung
  • ❌ Độ phức tạp trong quay vòng khóa

Phương pháp Khóa Công khai/Khóa Riêng

  • ✅ Không có bí mật chung
  • ✅ Dễ dàng quay vòng khóa
  • ✅ Tạo token phân tán
  • ❌ Xác thực hơi chậm hơn
  • ❌ Thiết lập phức tạp hơn

Các thực tiễn tốt nhất

🔐 An ninh

  1. Không bao giờ commit khóa riêng vào hệ thống quản lý phiên bản.
  2. Sử dụng biến môi trường cho dữ liệu nhạy cảm.
  3. Thực hiện thời gian hết hạn token (thời gian hợp lý).
  4. Xác thực tất cả các claims JWT (exp, iat, sub, v.v.).
  5. Sử dụng HTTPS trong môi trường sản xuất.
  6. Thực hiện giới hạn tần suất cho các điểm cuối xác thực.

🗂️ Quản lý Khóa

bash Copy
# Thêm vào .gitignore
echo "jwt-private.key" >> .gitignore
echo ".env" >> .gitignore

# Đặt quyền tệp phù hợp
chmod 600 jwt-private.key  # Khóa riêng - chỉ chủ sở hữu đọc/ghi
chmod 644 jwt-public.key   # Khóa công khai - có thể đọc bởi người khác

🏗️ Cấu trúc Token

Cấu trúc payload JWT được khuyến nghị:

json Copy
{
  "sub": "định_danh_người_dùng_unqiue",
  "username": "tên_dễ_đọc", 
  "iat": 1642000000,
  "exp": 1642086400,
  "roles": ["user", "translator"],
  "permissions": ["read", "translate"]
}

🔄 Làm mới Token

javascript Copy
// Kiểm tra thời gian hết hạn token trước khi thực hiện yêu cầu
function isTokenExpired(token) {
  try {
    const decoded = jwt.decode(token);
    return decoded.exp < Date.now() / 1000;
  } catch (error) {
    return true;
  }
}

// Logic tự động làm mới
if (isTokenExpired(currentToken)) {
  currentToken = await generateNewToken();
}

Khắc phục sự cố

Các vấn đề phổ biến

1. "Không cấu hình khóa công khai JWT"

bash Copy
# Giải pháp: Đặt khóa công khai trong môi trường
export JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----..."

2. "Xác thực token thất bại"

bash Copy
# Kiểm tra sự không tương thích thuật toán
# Đảm bảo token của bạn được ký bằng RS256 cho phương pháp khóa công khai/khóa riêng
# Đảm bảo token của bạn được ký bằng HS256 cho phương pháp khóa bí mật

3. "Token đã hết hạn"

javascript Copy
// Kiểm tra thời gian hết hạn token
const decoded = jwt.decode(token);
console.log('Token hết hạn vào lúc:', new Date(decoded.exp * 1000));

4. "Chữ ký không hợp lệ"

bash Copy
# Đảm bảo bạn đang sử dụng khóa riêng đúng cho việc ký
# Xác nhận khóa công khai khớp với khóa riêng đã sử dụng để ký
openssl rsa -in jwt-private.key -pubout | diff - jwt-public.key

Chế độ Gỡ lỗi

bash Copy
# Bật ghi log gỡ lỗi
DEBUG=jwt* npm start

# Kiểm tra chi tiết token mà không cần xác thực
node -e "console.log(JSON.stringify(require('jsonwebtoken').decode('token_của_bạn'), null, 2))"

Kiểm tra Các Điểm cuối

bash Copy
# Kiểm tra sức khỏe (không yêu cầu xác thực)
curl http://localhost:8000/health

# Trạng thái xác thực
curl http://localhost:8000/auth/status

# Xác thực token
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8000/auth/verify

🎯 Bước tiếp theo

  1. Chọn phương pháp dựa trên yêu cầu của bạn.
  2. Tạo các khóa phù hợp (khóa bí mật hoặc cặp RSA).
  3. Cấu hình biến môi trường.
  4. Kiểm tra việc tạo và xác thực token.
  5. Triển khai trong ứng dụng của bạn.
  6. Thiết lập giám sát và ghi log.

Danh sách kiểm tra sản xuất

  • Khóa được lưu trữ an toàn (biến môi trường, dịch vụ quản lý khóa).
  • Khóa riêng không bao giờ trên máy chủ (phương pháp khóa công khai/khóa riêng).
  • HTTPS được bật.
  • Thời gian hết hạn của token được cấu hình.
  • Giới hạn tần suất được thực hiện.
  • Ghi log và giám sát được thiết lập.
  • Xử lý lỗi được thực hiện.
  • Tài liệu được cập nhật.

Bài viết này đề cập đến cả hai phương pháp xác thực JWT. Hãy chọn phương pháp phù hợp nhất với kiến trúc và yêu cầu an ninh của bạn.

Nội dung bài viết

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào