Xác thực người dùng là một bước quan trọng trong quá trình đăng nhập, giúp hệ thống kiểm tra danh tính và cấp quyền truy cập. Khi người dùng nhập thông tin đăng nhập (tên đăng nhập và mật khẩu), ứng dụng sẽ đối chiếu dữ liệu này với thông tin đã lưu trong cơ sở dữ liệu. Nếu thông tin hợp lệ, hệ thống sẽ cho phép truy cập.
Trong nhiều ứng dụng web, dữ liệu xác thực thường được lưu trữ trong các hệ quản trị cơ sở dữ liệu quan hệ như MySQL hoặc PostgreSQL mà không sử dụng bộ nhớ đệm. Tuy nhiên, phương pháp này có một số hạn chế:
-
Tăng tải cho cơ sở dữ liệu: Mỗi lần người dùng đăng nhập, ứng dụng phải gửi yêu cầu xác thực đến máy chủ cơ sở dữ liệu, gây áp lực lên hệ thống, đặc biệt khi có nhiều truy vấn đồng thời.
-
Khó mở rộng quy mô: Khi số lượng yêu cầu tăng cao (hàng nghìn lượt đăng nhập mỗi giây), cơ sở dữ liệu dựa trên ổ đĩa có thể không xử lý kịp, làm giảm hiệu suất toàn hệ thống.
Để giải quyết những vấn đề trên, Redis là một giải pháp hiệu quả giúp tối ưu xác thực người dùng bằng cách lưu trữ thông tin đăng nhập trong bộ nhớ đệm. Điều này giúp ứng dụng truy xuất dữ liệu nhanh hơn, giảm tải đáng kể cho cơ sở dữ liệu gốc.
Redis là một hệ thống lưu trữ dữ liệu key-value hiệu suất cao, hoạt động trên bộ nhớ RAM, giúp tăng tốc xử lý phiên đăng nhập. Trong hướng dẫn này, bạn sẽ tìm hiểu cách tích hợp Redis vào ứng dụng Python/MySQL trên Ubuntu 22.04 để cải thiện tốc độ xác thực và tối ưu hiệu suất hệ thống.
Yêu cầu trước khi bắt đầu
Trước khi bắt đầu hướng dẫn, bạn cần chuẩn bị các bước sau:
-
Triển khai một máy chủ Ubuntu 22.04.
-
Chuyển sang tài khoản sudo mới và cài đặt:
Bước 1 – Cài đặt các trình điều khiển cơ sở dữ liệu Python cho Redis và MySQL
Ứng dụng này sẽ lưu trữ vĩnh viễn thông tin đăng nhập của người dùng, như tên và mật khẩu, trong máy chủ cơ sở dữ liệu MySQL. Khi người dùng đăng nhập, một script Python sẽ truy vấn cơ sở dữ liệu MySQL và so khớp thông tin với dữ liệu đã lưu. Sau đó, script Python sẽ lưu bộ nhớ đệm thông tin đăng nhập của người dùng trong cơ sở dữ liệu Redis để phục vụ các yêu cầu sau này.
Để hoàn thành quy trình này, các script Python của bạn cần có trình điều khiển cơ sở dữ liệu (các module Python) để giao tiếp với máy chủ MySQL và Redis. Thực hiện các bước sau:
Cập nhật chỉ mục thông tin gói và chạy lệnh sau để cài đặt python3-pip, trình quản lý gói Python giúp bạn cài đặt các module bổ sung không có sẵn trong thư viện tiêu chuẩn của Python.
sudo apt install python3-pip
Cài đặt trình điều khiển MySQL cho Python:
pip install mysql-connector-python
Cài đặt trình điều khiển Redis cho Python:
pip install redis
Sau khi cài đặt các trình điều khiển cần thiết để giao tiếp với MySQL và Redis, hãy chuyển sang bước tiếp theo để khởi tạo cơ sở dữ liệu MySQL.
Bước 2 – Thiết lập một cơ sở dữ liệu MySQL mẫu
Trong hướng dẫn này, bạn sẽ cần một bảng MySQL. Trong môi trường sản xuất, bạn có thể có hàng chục bảng phục vụ các yêu cầu khác nhau. Thiết lập một cơ sở dữ liệu và tạo bảng bằng cách thực hiện các lệnh sau:
Đăng nhập vào máy chủ cơ sở dữ liệu MySQL với tư cách người dùng root:
sudo mysql -u root -p
Nhập mật khẩu root
của MySQL khi được yêu cầu, sau đó chạy các lệnh sau để tạo cơ sở dữ liệu mẫu company
và tài khoản company_user
. Hãy thay thế example-mysql-password
bằng mật khẩu mạnh:
CREATE DATABASE company; CREATE USER 'company_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'example-mysql-password'; GRANT ALL PRIVILEGES ON company.* TO 'company_user'@'localhost'; FLUSH PRIVILEGES;
Bạn sẽ nhận được thông báo sau để xác nhận các lệnh đã chạy thành công:
OutputQuery OK, 1 row affected (0.01 sec)
Chuyển sang cơ sở dữ liệu company:
USE company;
Xác nhận rằng bạn đã kết nối thành công đến cơ sở dữ liệu mới với thông báo:
OutputDatabase changed
Tạo bảng system_users
. Cột user_id
sẽ đóng vai trò PRIMARY KEY
để định danh duy nhất cho mỗi người dùng. Các cột username
và password
chứa thông tin đăng nhập mà người dùng cần cung cấp để đăng nhập ứng dụng. Các cột first_name
và last_name
lưu trữ tên của người dùng:
custom_prefix(mysql>) CREATE TABLE system_users ( user_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50), first_name VARCHAR(50), last_name VARCHAR(50), password VARCHAR(50) ) ENGINE = InnoDB;
Xác nhận rằng bảng mới đã được tạo thành công với thông báo:
OutputQuery OK, 0 rows affected (0.03 sec)
Chèn dữ liệu mẫu vào bảng system_users. Sử dụng hàm MD5(…) tích hợp của MySQL để băm mật khẩu cho mục đích bảo mật:
INSERT INTO system_users (username, first_name, last_name, password) VALUES ('john_doe', 'JOHN', 'DOE', MD5('password_1')); INSERT INTO system_users (username, first_name, last_name, password) VALUES ('mary_henry', 'MARY', 'HENRY', MD5('password_2')); INSERT INTO system_users (username, first_name, last_name, password) VALUES ('peter_jade', 'PETER', 'JADE', MD5('password_3'));
Xác nhận kết quả với thông báo:
OutputQuery OK, 1 row affected (0.00 sec)
Truy vấn bảng system_users
để đảm bảo dữ liệu đã được chèn đúng:
SELECT user_id, first_name, last_name, password FROM system_users;
Kết quả truy vấn sẽ như sau:
Output+---------+------------+-----------+----------------------------------+ | user_id | first_name | last_name | password | +---------+------------+-----------+----------------------------------+ | 1 | JOHN | DOE | 57210b12af5e06ad2e6e54a93b1465aa | | 2 | MARY | HENRY | 259640f97ac2b4379dd540ff4016654c | | 3 | PETER | JADE | 48ef85c894a06a4562268de8e4d934e1 | +---------+------------+-----------+----------------------------------+ 3 rows in set (0.00 sec)
Đăng xuất khỏi máy chủ MySQL:
QUIT;
Bạn đã thiết lập thành công cơ sở dữ liệu MySQL cho ứng dụng. Trong bước tiếp theo, bạn sẽ xây dựng module Python để giao tiếp với cơ sở dữ liệu mẫu.
Bước 3 – Tạo Module cổng MySQL trung tâm cho Python
Khi viết bất kỳ dự án Python nào, bạn nên tạo một module riêng cho từng tác vụ nhằm tăng khả năng tái sử dụng mã nguồn. Trong bước này, bạn sẽ thiết lập một module trung tâm cho phép kết nối và truy vấn cơ sở dữ liệu MySQL từ một script Python. Thực hiện các bước sau:
Tạo một thư mục dự án. Thư mục này sẽ tách riêng các file mã nguồn Python của bạn khỏi các file hệ thống khác:
Chuyển sang thư mục dự án mới:
mkdir project
Sử dụng trình soạn thảo nano
để mở file mysql_db.py
, file này sẽ chứa module Python giao tiếp với cơ sở dữ liệu MySQL:
cd project
Nhập nội dung sau vào file mysql_db.py
. Thay thế example-mysql-password
bằng mật khẩu MySQL đúng của tài khoản company_user
:
import mysql.connector class MysqlDb: def db_con(self): mysql_con = mysql.connector.connect( host = "localhost", user = "company_user", password = "example-mysql-password", database = "company", port = "3306" ) return mysql_con def query(self, username, password): db = self.db_con() db_cursor = db.cursor() db_query = "select username, password from system_users where username = %s and password = md5(%s)" db_cursor.execute(db_query, (username, password)) result = db_cursor.fetchone() row_count = db_cursor.rowcount if row_count < 1: return False else: return result[1]
Lưu và đóng file mysql_db.py
.
Module mysql_db.py
có một lớp (MysqlDb
) với hai phương thức:
-
db_con(self)
: Kết nối tới cơ sở dữ liệucompany
đã tạo ở trên và trả về một kết nối MySQL có thể tái sử dụng. -
query(self, username, password)
: Nhậnusername
vàpassword
, truy vấn bảngsystem_users
để kiểm tra sự khớp. Nếu không tìm thấy người dùng, trả vềFalse
; nếu tìm thấy, trả về mật khẩu (result[1]).
Tiếp theo, hãy thiết lập một module Redis tương tự để giao tiếp với kho dữ liệu key-value Redis.
Bước 4 – Tạo Module Redis trung tâm cho Python
Trong bước này, bạn sẽ lập trình một module để kết nối với máy chủ Redis. Thực hiện các bước sau:
Mở file mới redis_db.py
:
nano redis_db.py
Nhập nội dung sau vào file redis_db.py
. Thay thế example-redis-password
bằng mật khẩu đúng cho máy chủ Redis:
import redis class RedisDb: def db_con(self): r_host = 'localhost' r_port = 6379 r_pass = 'example-redis-password' redis_con = redis.Redis(host = r_host, port = r_port, password = r_pass) return redis_con
Lưu và đóng file redis_db.py
.
File trên có một lớp (RedisDb
) với phương thức db_con(self)
, sử dụng thông tin xác thực được cung cấp để kết nối tới máy chủ Redis và trả về kết nối có thể tái sử dụng.
Sau khi thiết lập module Redis, hãy tạo file chính cho dự án của bạn.
Bước 5 – Tạo điểm vào cho ứng dụng
Mỗi ứng dụng Python đều cần có một điểm vào hoặc file chính để thực thi khi chạy ứng dụng. Trong file này, bạn sẽ viết mã hiển thị thời gian hiện tại của máy chủ cho người dùng đã xác thực. File này sử dụng các module MySQL và Redis tùy chỉnh mà bạn đã tạo để xác thực người dùng. Thực hiện các bước sau để tạo file:
Mở file mới index.py
:
nano index.py
Nhập nội dung sau vào file index.py
:
from encodings import utf_8 import base64 from hashlib import md5 import json import datetime import http.server from http import HTTPStatus import socketserver import mysql_db import redis_db class HttpHandler(http.server.SimpleHTTPRequestHandler): def do_GET(self): self.send_response(HTTPStatus.OK) self.send_header('Content-type', 'application/json') self.end_headers() authHeader = self.headers.get('Authorization').split(' '); auth_user, auth_password = base64.b64decode(authHeader[1]).decode('utf8').split(':') mysql_server = mysql_db.MysqlDb() redis_server = redis_db.RedisDb() redis_client = redis_server.db_con() now = datetime.datetime.now() current_time = now.strftime("%Y-%m-%d %H:%M:%S") resp = {} if redis_client.exists(auth_user): if md5(auth_password.encode('utf8')).hexdigest() != redis_client.get(auth_user).decode('utf8'): resp = {"error": "Invalid username/password."} else: resp = {"time": current_time, "authorized by": "Redis server"} else: mysql_resp = mysql_server.query(auth_user, auth_password) if mysql_resp == False: resp = {"error": "Invalid username/password."} else: resp = {"time": current_time, "authorized by": "MySQL server"} redis_client.set(auth_user, mysql_resp) self.wfile.write(bytes(json.dumps(resp, indent = 2) + "\r\n", "utf8")) httpd = socketserver.TCPServer(('', 8080), HttpHandler) print("Web server is running on port 8080...") try: httpd.serve_forever() except KeyboardInterrupt: httpd.server_close() print("Web server has stopped runing.")
Lưu và đóng file index.py
.
Trong file index.py
:
-
Phần
import
thêm các module cần thiết:-
utf_8
,base64
,md5
, vàjson
để xử lý mã hóa và định dạng văn bản. -
http.server
,HTTPStatus
, vàsocketserver
để xây dựng máy chủ web. -
datetime
để xử lý ngày giờ. -
mysql_db
vàredis_db
là các module tùy chỉnh mà bạn đã tạo để truy cập MySQL và Redis.
-
-
Lớp
HttpHandler(http.server.SimpleHTTPRequestHandler)
xử lý các yêu cầu HTTP GET và hiển thị thời gian hiện tại của máy chủ cho người dùng đã xác thực. -
Trong phần kiểm tra, nếu thông tin người dùng tồn tại trong Redis (
if redis_client.exists(auth_user):
), sẽ kiểm tra xem mật khẩu được lưu trong Redis có khớp với mật khẩu người dùng cung cấp hay không. Nếu không khớp, trả về thông báo lỗi"Invalid username/password."
; nếu khớp, trả về thời gian hiện tại kèm thông tin xác thực từ Redis. -
Bước 6 – Kiểm tra ứng dụng
Trong bước này, bạn sẽ chạy ứng dụng để kiểm tra cơ chế lưu bộ nhớ đệm của Redis có hoạt động hay không. Thực hiện các lệnh sau:
Chạy ứng dụng bằng lệnh:
python3 index.py
Xác nhận rằng máy chủ web tùy chỉnh của ứng dụng đang chạy với thông báo:
OutputWeb server is running on port 8080...
Mở một kết nối SSH khác đến máy chủ của bạn trong một cửa sổ terminal mới và chạy các lệnh curl sau để gửi bốn yêu cầu GET sử dụng thông tin đăng nhập của john_doe
. Thêm [1-4] vào cuối URL http://localhost:8080/
để gửi bốn yêu cầu trong một lệnh:
curl -X GET -u john_doe:password_1 http://localhost:8080/[1-4]
Xác nhận kết quả như sau. Máy chủ MySQL chỉ phục vụ yêu cầu xác thực đầu tiên, sau đó cơ sở dữ liệu Redis phục vụ 3 yêu cầu tiếp theo.
Output[1/4] { "time": "2023-11-07 10:04:38", "authorized by": "MySQL server" } [4/4] { "time": "2023-11-07 10:04:38", "authorized by": "Redis server" }
Ứng dụng của bạn đã hoạt động đúng như mong đợi.
Kết luận
Trong hướng dẫn này, bạn đã xây dựng một ứng dụng Python sử dụng máy chủ Redis để lưu bộ nhớ đệm thông tin đăng nhập của người dùng. Redis là một máy chủ cơ sở dữ liệu có khả năng sẵn sàng cao và mở rộng quy mô, có thể xử lý hàng nghìn giao dịch mỗi giây. Với cơ chế lưu bộ nhớ đệm của Redis, bạn có thể giảm đáng kể lưu lượng truy cập tới máy chủ cơ sở dữ liệu phía sau.