AI Hunter
Member
Hiện tại, hệ thống của chúng ta đang giống như một ngôi nhà không khóa cửa. Ai biết IP là vào được.
Hôm nay, chúng ta sẽ lắp cửa sắt và khóa số.
Chúng ta sẽ áp dụng mô hình Microservice Security:
Chainlit sẽ giữ chiếc chìa khóa này và thay mặt User gọi xuống Backend.
Chúng ta sẽ dùng cơ chế Bearer Token đơn giản (một dạng Static Token) để bảo vệ API.
Mở file
Từ giờ, nếu ai gọi vào
Chainlit hỗ trợ sẵn tính năng đăng nhập cực xịn.
Mở file
Bây giờ chúng ta cần cung cấp "mật khẩu chung" cho cả 2 container qua biến môi trường.
Sửa file
Chạy lại Docker:
Bây giờ, khi truy cập http://localhost:8501:
Vậy là Jarvis đã được trang bị "áo giáp". Giờ đây bạn có thể yên tâm mở port trên Router để truy cập Jarvis từ xa, hoặc deploy lên VPS mà không sợ bị người lạ phá đám.
Hôm nay, chúng ta sẽ lắp cửa sắt và khóa số.
1. Chiến lược bảo mật
Chúng ta sẽ áp dụng mô hình Microservice Security:
- Frontend (Chainlit): Đóng vai trò là "Cổng chính". Người dùng (User) phải có Username/Password mới được vào.
- Backend (FastAPI): Đóng vai trò là "Két sắt". Nó không quan tâm User là ai, nó chỉ quan tâm người gọi nó có cầm "Chìa khóa vạn năng" (API Secret Key) hay không.
Chainlit sẽ giữ chiếc chìa khóa này và thay mặt User gọi xuống Backend.
2. Bước 1: Khóa Backend (FastAPI)
Chúng ta sẽ dùng cơ chế Bearer Token đơn giản (một dạng Static Token) để bảo vệ API.
Mở file
backend/server.py và thêm "ổ khóa":
Python:
from fastapi import FastAPI, HTTPException, Depends, Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel
import os
app = FastAPI()
security = HTTPBearer()
# Lấy mật khẩu API từ biến môi trường (Docker sẽ truyền vào)
JARVIS_API_SECRET = os.getenv("JARVIS_API_SECRET", "mac-dinh-khong-an-toan")
# --- HÀM BẢO VỆ (GARDEN GUARD) ---
def verify_token(credentials: HTTPAuthorizationCredentials = Security(security)):
token = credentials.credentials
if token != JARVIS_API_SECRET:
raise HTTPException(
status_code=403,
detail="⛔ Truy cập bị từ chối! Sai mật khẩu API."
)
return token
class ChatRequest(BaseModel):
message: str
# Thêm dependencies=[Depends(verify_token)] để khóa endpoint này lại
@app.post("/chat", dependencies=[Depends(verify_token)])
async def chat_endpoint(req: ChatRequest):
# Logic xử lý cũ giữ nguyên
return {"answer": f"Xin chào, tôi đã nhận lệnh: {req.message}"}
Từ giờ, nếu ai gọi vào
/chat mà không có Header Authorization: Bearer <JARVIS_API_SECRET>, họ sẽ bị đá ra ngay lập tức (Lỗi 403).3. Bước 2: Khóa Frontend (Chainlit)
Chainlit hỗ trợ sẵn tính năng đăng nhập cực xịn.
Mở file
frontend/app.py:
Python:
import chainlit as cl
import requests
import os
API_URL = os.getenv("API_URL", "http://localhost:8000/chat")
# Lấy chìa khóa để lát nữa gửi cho Backend
BACKEND_KEY = os.getenv("JARVIS_API_SECRET", "mac-dinh-khong-an-toan")
# 1. Cấu hình xác thực User (Login)
@cl.password_auth_callback
def auth_callback(username, password):
# Ở đây mình hardcode demo. Thực tế nên check từ DB hoặc biến môi trường.
# User: admin, Pass: jarvis123
if username == "admin" and password == "jarvis123":
return cl.User(identifier=username, metadata={"role": "admin"})
return None # Đăng nhập thất bại
@cl.on_chat_start
async def start():
await cl.Message(content="🔒 Hệ thống đã được bảo mật. Chào mừng sếp quay lại!").send()
@cl.on_message
async def main(message: cl.Message):
# Chuẩn bị Header chứa chìa khóa
headers = {
"Authorization": f"Bearer {BACKEND_KEY}",
"Content-Type": "application/json"
}
payload = {"message": message.content}
msg = cl.Message(content="")
await msg.send()
try:
# Gửi request kèm Header xác thực
response = requests.post(API_URL, json=payload, headers=headers)
if response.status_code == 200:
data = response.json()
msg.content = data.get("answer", "")
elif response.status_code == 403:
msg.content = "⛔ Lỗi bảo mật: Sai API Key kết nối Backend!"
else:
msg.content = f"Lỗi Server: {response.text}"
await msg.update()
except Exception as e:
msg.content = f"Lỗi kết nối: {str(e)}"
await msg.update()
4. Bước 3: Cập nhật Docker Compose
Bây giờ chúng ta cần cung cấp "mật khẩu chung" cho cả 2 container qua biến môi trường.
Sửa file
docker-compose.yml:
Mã:
version: '3.8'
services:
backend:
build: ./backend
ports:
- "8000:8000"
environment:
- JARVIS_API_SECRET=sieumatkhau123456 # <--- Mật khẩu Backend
# ... các biến khác giữ nguyên
frontend:
build: ./frontend
ports:
- "8501:8000"
environment:
- API_URL=http://backend:8000/chat
- JARVIS_API_SECRET=sieumatkhau123456 # <--- Phải trùng với Backend
- CHAINLIT_AUTH_SECRET=randomstring123 # Chainlit cần cái này để mã hóa session
depends_on:
- backend
5. Trải nghiệm
Chạy lại Docker:
Mã:
docker-compose up --build
Bây giờ, khi truy cập http://localhost:8501:
- Bạn sẽ bị chặn lại ở màn hình Login.
- Nhập User:
admin/ Pass:jarvis123. - Vào trong chat bình thường.
- Thử dùng Postman gọi trực tiếp vào
http://localhost:8000/chatmà không có Header -> Bị chặn ngay lập tức.
Tổng kết
Vậy là Jarvis đã được trang bị "áo giáp". Giờ đây bạn có thể yên tâm mở port trên Router để truy cập Jarvis từ xa, hoặc deploy lên VPS mà không sợ bị người lạ phá đám.
Bài viết liên quan