6.1 Xác thực và phân quyền người dùng
Ở giai đoạn này, chúng ta sẽ tìm hiểu cách bảo mật ứng dụng đa container và quản lý quyền truy cập. Điều này bao gồm xác thực người dùng, mã hóa dữ liệu, bảo vệ API và thiết lập kết nối an toàn giữa các dịch vụ.
Mục tiêu: đảm bảo rằng chỉ những người dùng đã đăng ký và được xác thực mới có thể tương tác với ứng dụng và thực hiện các thao tác.
Triển khai JWT (JSON Web Token) cho xác thực
Bước 1. Cài đặt các thư viện cần thiết:
pip install Flask-JWT-Extended
Bước 2. Cấu hình JWT trong ứng dụng Flask:
Thêm các thay đổi sau vào file backend/app/__init__.py
:
from flask_jwt_extended import JWTManager
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://taskuser:taskpassword@database:5432/taskdb'
app.config['JWT_SECRET_KEY'] = 'your_jwt_secret_key' # Thay bằng khóa bí mật của bạn
db = SQLAlchemy(app)
jwt = JWTManager(app)
from app import routes
Bước 3. Tạo các route cho đăng ký và đăng nhập:
Thêm các route sau vào file backend/app/routes.py
:
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from werkzeug.security import generate_password_hash, check_password_hash
from app.models import User, Task
@app.route('/register', methods=['POST'])
def register():
data = request.get_json()
hashed_password = generate_password_hash(data['password'], method='sha256')
new_user = User(username=data['username'], password=hashed_password)
db.session.add(new_user)
db.session.commit()
return jsonify({'message': 'Người dùng đã đăng ký thành công'}), 201
@app.route('/login', methods=['POST'])
def login():
data = request.get_json()
user = User.query.filter_by(username=data['username']).first()
if not user or not check_password_hash(user.password, data['password']):
return jsonify({'message': 'Thông tin đăng nhập không hợp lệ'}), 401
access_token = create_access_token(identity=user.id)
return jsonify({'access_token': access_token}), 200
@app.route('/tasks', methods=['GET'])
@jwt_required()
def get_tasks():
current_user_id = get_jwt_identity()
tasks = Task.query.filter_by(owner_id=current_user_id).all()
return jsonify([task.to_dict() for task in tasks])
Bước 4. Cập nhật model User để lưu trữ mật khẩu:
Cập nhật file backend/app/models.py
:
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
tasks = db.relationship('Task', backref='owner', lazy=True)
6.2 Mã hóa dữ liệu
Mục tiêu: đảm bảo bảo vệ dữ liệu khi truyền giữa client và server.
Sử dụng HTTPS
Bước 1. Cấu hình Nginx làm reverse proxy hỗ trợ HTTPS:
Tạo file nginx.conf
trong thư mục gốc của dự án:
server {
listen 80;
server_name your_domain.com;
location / {
proxy_pass http://frontend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api {
proxy_pass http://backend:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Bước 2. Tạo Dockerfile cho Nginx:
Tạo file Dockerfile
trong thư mục nginx
:
FROM nginx:latest
COPY nginx.conf /etc/nginx/nginx.conf
Bước 3. Thêm Nginx vào compose.yaml
:
Cập nhật file compose.yaml
, thêm service cho Nginx:
version: '3'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
networks:
- task-network
backend:
build: ./backend
ports:
- "5000:5000"
depends_on:
- database
networks:
- task-network
environment:
- DATABASE_URL=postgresql://taskuser:taskpassword@database:5432/taskdb
database:
image: postgres:13
environment:
- POSTGRES_DB=taskdb
- POSTGRES_USER=taskuser
- POSTGRES_PASSWORD=taskpassword
networks:
- task-network
volumes:
- db-data:/var/lib/postgresql/data
nginx:
build: ./nginx
ports:
- "80:80"
depends_on:
- frontend
- backend
networks:
- task-network
networks:
task-network:
driver: bridge
volumes:
db-data:
6.3 Nhận SSL Certificate bằng Let's Encrypt
Bước 1. Cài đặt Certbot:
Làm theo hướng dẫn trên trang web chính thức của Certbot để cài đặt Certbot.
Bước 2. Nhận chứng chỉ:
sudo certbot certonly --standalone -d your_domain.com
Bước 3. Cấu hình Nginx để sử dụng SSL:
Cập nhật nginx.conf
để sử dụng SSL:
server {
listen 80;
server_name your_domain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name your_domain.com;
ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
location / {
proxy_pass http://frontend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api {
proxy_pass http://backend:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Bước 4. Cập nhật Dockerfile cho Nginx để copy chứng chỉ:
FROM nginx:latest
COPY nginx.conf /etc/nginx/nginx.conf
COPY /etc/letsencrypt /etc/letsencrypt
6.4 Bảo vệ API
Mục tiêu: giới hạn quyền truy cập vào API và ngăn chặn các yêu cầu không được phép.
Sử dụng JWT để bảo vệ các route
Chúng ta đã thêm bảo vệ cho các route của task bằng cách sử dụng decorator @jwt_required()
. Đảm bảo rằng tất cả các route nhạy cảm đều được bảo vệ bởi decorator này:
from flask_jwt_extended import jwt_required, get_jwt_identity
@app.route('/tasks', methods=['GET'])
@jwt_required()
def get_tasks():
current_user_id = get_jwt_identity()
tasks = Task.query.filter_by(owner_id=current_user_id).all()
return jsonify([task.to_dict() for task in tasks])
Hạn chế quyền truy cập vào cơ sở dữ liệu
Mục tiêu: ngăn chặn truy cập trái phép vào cơ sở dữ liệu.
Cấu hình vai trò và quyền
Bước 1. Tạo user với quyền hạn chế:
CREATE USER limited_user WITH PASSWORD 'limited_password';
GRANT CONNECT ON DATABASE taskdb TO limited_user;
GRANT USAGE ON SCHEMA public TO limited_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO limited_user;
Bước 2. Cập nhật biến môi trường DATABASE_URL
:
Cập nhật biến môi trường DATABASE_URL
trong file compose.yaml
:
environment:
- DATABASE_URL=postgresql://limited_user:limited_password@database:5432/taskdb
GO TO FULL VERSION