Trong hướng dẫn này, chúng ta sẽ tạo một hệ thống đăng nhập và đăng ký hoàn chỉnh với Python Flask và MySQL .
Python hiện đang chiếm ưu thế trong lĩnh vực phát triển web, tăng trưởng đều đặn hàng năm. Nó có đường cong học tập thấp và do đó dễ thích nghi hơn nhiều so với các ngôn ngữ đã biết khác (Node.js, PHP, v.v.). Ngoài ra, Python còn tương đối mới nên sẽ rất thuận lợi nếu bạn bắt đầu học ngay lập tức.
Bắt đầu
Có một số bước chúng ta cần thực hiện trước khi tạo hệ thống đăng nhập và đăng ký python. Chúng tôi cần tải xuống và thiết lập Python cũng như cài đặt các gói mà ứng dụng của chúng tôi sẽ phụ thuộc vào.Bạn sẽ học được gì trong hướng dẫn này
- Form Design — Thiết kế biểu mẫu đăng nhập và đăng ký bằng HTML5 và CSS3.
- Templates — Tạo mẫu Flask bằng HTML và Python.
- Basic Validation — Xác thực dữ liệu biểu mẫu được gửi đến máy chủ (tên người dùng, mật khẩu và email).
- Session Management - Khởi tạo phiên và lưu trữ kết quả cơ sở dữ liệu đã truy xuất.
- MySQL Queries — Chọn và chèn các bản ghi từ/trong bảng cơ sở dữ liệu của chúng tôi.
- Routes - Định tuyến sẽ cho phép chúng tôi liên kết các URL với các chức năng mà chúng tôi sẽ tạo.
Yêu cầu
Tải xuống và cài đặt Python, đối với hướng dẫn này, tôi sẽ sử dụng Python 3.7.2. Đảm bảo kiểm tra tùy chọn Add Python to PATH trên màn hình thiết lập cài đặt.
Tải xuống và cài đặt MySQL Community Server và MySQL Workbench. Bạn có thể bỏ qua bước này nếu đã cài đặt máy chủ MySQL.
Mở dòng lệnh và Cài đặt Python Flask bằng lệnh: pip install flask
Cài đặt Flask-MySQLdb bằng lệnh: pip install flask-mysqldb
Tải xuống và cài đặt MySQL Community Server và MySQL Workbench. Bạn có thể bỏ qua bước này nếu đã cài đặt máy chủ MySQL.
Mở dòng lệnh và Cài đặt Python Flask bằng lệnh: pip install flask
Cài đặt Flask-MySQLdb bằng lệnh: pip install flask-mysqldb
Cấu trúc & thiết lập
Chúng ta cần tạo thư mục và tệp dự án của mình. Bạn có thể tạo thư mục ở bất cứ đâu trên máy tính của mình, miễn là Python có thể truy cập nó. Tạo các thư mục và tệp bên dưới.\-- pythonlogin |-- main.py \-- static |-- style.css \-- templates |-- index.html |-- register.html |-- home.html |-- profile.html |-- layout.html
- main.py — Đây sẽ là tệp dự án chính của chúng tôi, tất cả mã Python của chúng tôi sẽ nằm trong tệp này (Routes, MySQL connection, validation, v.v.).
- index.html — Mẫu biểu mẫu đăng nhập được tạo bằng HTML5 và CSS3.
- register.html — Mẫu biểu mẫu đăng ký được tạo bằng HTML5 và CSS3.
- home.html — Mẫu trang chủ được giới hạn cho người dùng đã đăng nhập.
- profile.html — Mẫu hồ sơ được giới hạn cho người dùng đã đăng nhập. Thông tin chi tiết của người dùng sẽ được điền trên trang này.
- layout.html — Mẫu bố cục cho trang chủ và mẫu hồ sơ.
- style.css — Biểu định kiểu CSS3 cho hệ thống đăng nhập và đăng ký của chúng tôi.
- Đảm bảo máy chủ MySQL của bạn đang hoạt động. Nó sẽ tự động bắt đầu nếu bạn cài đặt nó thông qua trình cài đặt. Ngoài ra, đảm bảo MySQL đang chạy trên cổng 3306, nếu không bạn sẽ gặp lỗi kết nối.
- Mở Command Prompt và điều hướng đến thư mục dự án của bạn. Bạn có thể làm điều này với lệnh cd c:\your_project_folder_destination trên Windows.
- Chạy lệnh: set FLASK_APP=main.py
- Chạy lệnh: set FLASK_DEBUG=1
- Chạy lệnh: flask run
Tạo Database và thiết lập các Table
MySQL Workbench là một GUI để quản lý cơ sở dữ liệu của chúng tôi. Thực hiện theo các hướng dẫn dưới đây về cách bắt đầu.- Mở MySQL Workbench
- Nhập chi tiết MySQL của bạn
- Nhấp vào Test Connection. Nếu thành công, bạn có thể bấm OK
- Mở kết nối của bạn
- Thực hiện câu lệnh SQL sau:
CREATE DATABASE IF NOT EXISTS `pythonlogin` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; USE `pythonlogin`; CREATE TABLE IF NOT EXISTS `accounts` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `password` varchar(255) NOT NULL, `email` varchar(100) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO `accounts` (`id`, `username`, `password`, `email`) VALUES (1, 'test', 'test', 'test@test.com');
Tạo biểu định kiểu (CSS3)
Chỉnh sửa tệp style.css và thêm vào như sau:* { box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "segoe ui", roboto, oxygen, ubuntu, cantarell, "fira sans", "droid sans", "helvetica neue", Arial, sans-serif; font-size: 16px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } body { background-color: #435165; margin: 0; } .login, .register { width: 400px; background-color: #ffffff; box-shadow: 0 0 9px 0 rgba(0, 0, 0, 0.3); margin: 100px auto; } .login h1, .register h1 { text-align: center; color: #5b6574; font-size: 24px; padding: 20px 0 20px 0; border-bottom: 1px solid #dee0e4; } .login .links, .register .links { display: flex; padding: 0 15px; } .login .links a, .register .links a { color: #adb2ba; text-decoration: none; display: inline-flex; padding: 0 10px 10px 10px; font-weight: bold; } .login .links a:hover, .register .links a:hover { color: #9da3ac; } .login .links a.active, .register .links a.active { border-bottom: 3px solid #3274d6; color: #3274d6; } .login form, .register form { display: flex; flex-wrap: wrap; justify-content: center; padding-top: 20px; } .login form label, .register form label { display: flex; justify-content: center; align-items: center; width: 50px; height: 50px; background-color: #3274d6; color: #ffffff; } .login form input[type="password"], .login form input[type="text"], .login form input[type="email"], .register form input[type="password"], .register form input[type="text"], .register form input[type="email"] { width: 310px; height: 50px; border: 1px solid #dee0e4; margin-bottom: 20px; padding: 0 15px; } .login form input[type="submit"], .register form input[type="submit"] { width: 100%; padding: 15px; margin-top: 20px; background-color: #3274d6; border: 0; cursor: pointer; font-weight: bold; color: #ffffff; transition: background-color 0.2s; } .login form input[type="submit"]:hover, .register form input[type="submit"]:hover { background-color: #2868c7; transition: background-color 0.2s; } .navtop { background-color: #2f3947; height: 60px; width: 100%; border: 0; } .navtop div { display: flex; margin: 0 auto; width: 1000px; height: 100%; } .navtop div h1, .navtop div a { display: inline-flex; align-items: center; } .navtop div h1 { flex: 1; font-size: 24px; padding: 0; margin: 0; color: #eaebed; font-weight: normal; } .navtop div a { padding: 0 20px; text-decoration: none; color: #c1c4c8; font-weight: bold; } .navtop div a i { padding: 2px 8px 0 0; } .navtop div a:hover { color: #eaebed; } body.loggedin { background-color: #f3f4f7; } .content { width: 1000px; margin: 0 auto; } .content h2 { margin: 0; padding: 25px 0; font-size: 22px; border-bottom: 1px solid #e0e0e3; color: #4a536e; } .content > p, .content > div { box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.1); margin: 25px 0; padding: 25px; background-color: #fff; } .content > p table td, .content > div table td { padding: 5px; } .content > p table td:first-child, .content > div table td:first-child { font-weight: bold; color: #4a536e; padding-right: 15px; } .content > div p { padding: 5px; margin: 0 0 10px 0; }
Tạo hệ thống đăng nhập
Cuối cùng thì chúng ta cũng có thể bắt đầu viết mã bằng Python! Những gì chúng ta sẽ làm trong phần này là tạo mẫu đăng nhập, kết nối với cơ sở dữ liệu MySQL, triển khai xác thực đăng nhập và xác định các biến phiên.Điều đầu tiên chúng ta cần làm là nhập các gói chúng ta sẽ sử dụng, chỉnh sửa tệp main.py và thêm vào như sau:
from flask import Flask, render_template, request, redirect, url_for, session from flask_mysqldb import MySQL import MySQLdb.cursors import re
Thêm vào sau:
app = Flask(__name__) # Change this to your secret key (can be anything, it's for extra protection) app.secret_key = 'your secret key' # Enter your database connection details below app.config['MYSQL_HOST'] = 'localhost' app.config['MYSQL_USER'] = 'root' app.config['MYSQL_PASSWORD'] = '' app.config['MYSQL_DB'] = 'pythonlogin' # Intialize MySQL mysql = MySQL(app)
Bây giờ chúng ta có thể tiến hành tạo trang đăng nhập. Để làm điều đó, chúng ta cần tạo một tuyến đường mới. Các tuyến đường sẽ cho phép chúng tôi liên kết các chức năng của mình với một URL cụ thể.
Thêm vào sau:
# http://localhost:5000/pythonlogin/ - the following will be our login page, which will use both GET and POST requests @app.route('/pythonlogin/', methods=['GET', 'POST']) def login(): # Output message if something goes wrong... msg = '' return render_template('index.html', msg='')
Tạo mẫu đăng nhập
Chỉnh sửa tệp index.html và thêm:<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Login</title> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css"> </head> <body> <div class="login"> <h1>Login</h1> <div class="links"> <a href="{{ url_for('login') }}" class="active">Login</a> <a href="#">Register</a> </div> <form action="{{ url_for('login') }}" method="post"> <label for="username"> <i class="fas fa-user"></i> </label> <input type="text" name="username" placeholder="Username" id="username" required> <label for="password"> <i class="fas fa-lock"></i> </label> <input type="password" name="password" placeholder="Password" id="password" required> <div class="msg">{{ msg }}</div> <input type="submit" value="Login"> </form> </div> </body> </html>
Nếu chúng tôi điều hướng đến http://localhost:5000/pythonlogin/ trong trình duyệt web của mình, nó sẽ giống như sau:
Nếu bạn nhấp vào nút Login, sẽ không có gì xảy ra hoặc sẽ trả về lỗi, đó là do chúng tôi chưa triển khai mã xử lý yêu cầu POST.Xác thực người dùng bằng Python
Bây giờ chúng ta cần quay lại tệp main.py của mình và thêm mã xác thực vào chức năng định tuyến mà chúng ta đã tạo.def login(): # Output message if something goes wrong... msg = '' # Check if "username" and "password" POST requests exist (user submitted form) if request.method == 'POST' and 'username' in request.form and 'password' in request.form: # Create variables for easy access username = request.form['username'] password = request.form['password']
# Check if account exists using MySQL cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT * FROM accounts WHERE username = %s AND password = %s', (username, password,)) # Fetch one record and return result account = cursor.fetchone()
# If account exists in accounts table in out database if account: # Create session data, we can access this data in other routes session['loggedin'] = True session['id'] = account['id'] session['username'] = account['username'] # Redirect to home page return 'Logged in successfully!' else: # Account doesnt exist or username/password incorrect msg = 'Incorrect username/password!'
Các biến phiên về cơ bản hoạt động giống như cookie của trình duyệt. Chúng được lưu trữ trên máy chủ thay vì trình duyệt của người dùng.
Nếu tài khoản không tồn tại, chúng tôi chỉ cần xuất lỗi trên biểu mẫu đăng nhập.
Lộ trình đăng nhập của bạn sẽ giống như sau:
@app.route('/pythonlogin/', methods=['GET', 'POST']) def login(): # Output message if something goes wrong... msg = '' # Check if "username" and "password" POST requests exist (user submitted form) if request.method == 'POST' and 'username' in request.form and 'password' in request.form: # Create variables for easy access username = request.form['username'] password = request.form['password'] # Check if account exists using MySQL cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT * FROM accounts WHERE username = %s AND password = %s', (username, password,)) # Fetch one record and return result account = cursor.fetchone() # If account exists in accounts table in out database if account: # Create session data, we can access this data in other routes session['loggedin'] = True session['id'] = account['id'] session['username'] = account['username'] # Redirect to home page return 'Logged in successfully!' else: # Account doesnt exist or username/password incorrect msg = 'Incorrect username/password!' # Show the login form with message (if any) return render_template('index.html', msg=msg)
Tạo tập lệnh đăng xuất
Để người dùng đăng xuất, tất cả những gì chúng ta phải làm là xóa các biến phiên đã được tạo khi người dùng đăng nhập.Thêm đoạn mã sau vào tệp main.py:
# http://localhost:5000/python/logout - this will be the logout page @app.route('/pythonlogin/logout') def logout(): # Remove session data, this will log the user out session.pop('loggedin', None) session.pop('id', None) session.pop('username', None) # Redirect to login page return redirect(url_for('login'))
Chúng tôi có thể đăng xuất bằng cách điều hướng đến URL sau: http://localhost:5000/pythonlogin/logout
Tạo hệ thống đăng ký
Chúng tôi cần một hệ thống đăng ký mà người dùng có thể sử dụng để đăng ký trên ứng dụng của chúng tôi. Những gì chúng ta sẽ làm trong phần này là tạo một route đăng ký mới và tạo mẫu đăng ký, cùng với biểu mẫu đăng ký, sẽ bao gồm các trường nhập, nút gửi, v.v.Tạo mẫu đăng ký
Chỉnh sửa tệp register.html và thêm:<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Register</title> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css"> </head> <body> <div class="register"> <h1>Register</h1> <div class="links"> <a href="{{ url_for('login') }}">Login</a> <a href="{{ url_for('register') }}" class="active">Register</a> </div> <form action="{{ url_for('register') }}" method="post" autocomplete="off"> <label for="username"> <i class="fas fa-user"></i> </label> <input type="text" name="username" placeholder="Username" id="username" required> <label for="password"> <i class="fas fa-lock"></i> </label> <input type="password" name="password" placeholder="Password" id="password" required> <label for="email"> <i class="fas fa-envelope"></i> </label> <input type="email" name="email" placeholder="Email" id="email" required> <div class="msg">{{ msg }}</div> <input type="submit" value="Register"> </form> </div> </body> </html>
Thuộc tính hành động của biểu mẫu được liên kết với tuyến "register ", vì chúng tôi sẽ sử dụng tuyến này để xử lý yêu cầu POST.
Đăng ký người dùng với Python
Bây giờ chúng ta đã tạo mẫu của mình, chúng ta có thể tiến hành tạo tuyến "register ", tuyến này sẽ xử lý yêu cầu POST và chèn một tài khoản mới vào bảng accounts của chúng ta , nhưng chỉ khi các trường đã gửi là hợp lệ.Quay trở lại tệp main.py và thêm vào như sau:
# http://localhost:5000/pythinlogin/register - this will be the registration page, we need to use both GET and POST requests @app.route('/pythonlogin/register', methods=['GET', 'POST']) def register(): # Output message if something goes wrong... msg = '' # Check if "username", "password" and "email" POST requests exist (user submitted form) if request.method == 'POST' and 'username' in request.form and 'password' in request.form and 'email' in request.form: # Create variables for easy access username = request.form['username'] password = request.form['password'] email = request.form['email'] elif request.method == 'POST': # Form is empty... (no POST data) msg = 'Please fill out the form!' # Show registration form with message (if any) return render_template('register.html', msg=msg)
# http://localhost:5000/pythinlogin/register - this will be the registration page, we need to use both GET and POST requests @app.route('/pythonlogin/register', methods=['GET', 'POST']) def register(): # Output message if something goes wrong... msg = '' # Check if "username", "password" and "email" POST requests exist (user submitted form) if request.method == 'POST' and 'username' in request.form and 'password' in request.form and 'email' in request.form: # Create variables for easy access username = request.form['username'] password = request.form['password'] email = request.form['email'] # Check if account exists using MySQL cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT * FROM accounts WHERE username = %s', (username,)) account = cursor.fetchone() # If account exists show error and validation checks if account: msg = 'Account already exists!' elif not re.match(r'[^@]+@[^@]+\.[^@]+', email): msg = 'Invalid email address!' elif not re.match(r'[A-Za-z0-9]+', username): msg = 'Username must contain only characters and numbers!' elif not username or not password or not email: msg = 'Please fill out the form!' else: # Account doesnt exists and the form data is valid, now insert new account into accounts table cursor.execute('INSERT INTO accounts VALUES (NULL, %s, %s, %s)', (username, password, email,)) mysql.connection.commit() msg = 'You have successfully registered!' elif request.method == 'POST': # Form is empty... (no POST data) msg = 'Please fill out the form!' # Show registration form with message (if any) return render_template('register.html', msg=msg)
Sau đó, mã sẽ chèn một tài khoản mới vào bảng accounts của chúng tôi .
Để kiểm tra xem nó có hoạt động chính xác hay không, hãy điều hướng đến http://localhost:5000/pythonlogin/register và điền vào biểu mẫu rồi nhấp vào nút Register. Bạn sẽ nhận được phản hồi sau:Bây giờ chúng ta có thể quay lại tệp index.html và thay đổi dòng này:
<a href="#">Register</a></pre>
<a href="{{ url_for('register') }}">Register</a>
Tạo trang chủ
Trang chủ sẽ chỉ dành cho người dùng đã đăng nhập. Người dùng không đăng ký không thể truy cập trang này. Bạn có thể điều chỉnh trang này và tạo nhiều trang hơn.Chỉnh sửa tệp main.py và thêm vào như sau:
# http://localhost:5000/pythinlogin/home - this will be the home page, only accessible for loggedin users @app.route('/pythonlogin/home') def home(): # Check if user is loggedin if 'loggedin' in session: # User is loggedin show them the home page return render_template('home.html', username=session['username']) # User is not loggedin redirect to login page return redirect(url_for('login'))
Chỉnh sửa tệp home.html và thêm vào như sau:
{% extends 'layout.html' %} {% block title %}Home{% endblock %} {% block content %} <h2>Home Page</h2> <p>Welcome back, {{ username }}!</p> {% endblock %}
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{% block title %}{% endblock %}</title> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css"> </head> <body class="loggedin"> <nav class="navtop"> <div> <h1>Website Title</h1> <a href="{{ url_for('home') }}"><i class="fas fa-home"></i>Home</a> <a href="{{ url_for('profile') }}"><i class="fas fa-user-circle"></i>Profile</a> <a href="{{ url_for('logout') }}"><i class="fas fa-sign-out-alt"></i>Logout</a> </div> </nav> <div class="content"> {% block content %}{% endblock %} </div> </body> </html>
Hiện tại, khi người dùng đăng nhập, sẽ có một thông báo đầu ra cơ bản, giờ đây chúng tôi có thể thay đổi điều đó để chuyển hướng người dùng đến trang chủ mới của chúng tôi. Tìm đoạn mã sau trong chức năng lộ trình đăng nhập:
return 'Logged in successfully!'
return redirect(url_for('home'))
Tiếp theo, chúng ta sẽ tạo trang hồ sơ và điền thông tin chi tiết của người dùng.
Tạo trang Hồ sơ
Trang hồ sơ sẽ điền tất cả các chi tiết liên quan đến tài khoản (tên người dùng, mật khẩu và email).Thêm tuyến sau vào tệp main.py:
# http://localhost:5000/pythinlogin/profile - this will be the profile page, only accessible for loggedin users @app.route('/pythonlogin/profile') def profile(): # Check if user is loggedin if 'loggedin' in session: # We need all the account info for the user so we can display it on the profile page cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT * FROM accounts WHERE id = %s', (session['id'],)) account = cursor.fetchone() # Show the profile page with account info return render_template('profile.html', account=account) # User is not loggedin redirect to login page return redirect(url_for('login'))
{% extends 'layout.html' %} {% block title %}Profile{% endblock %} {% block content %} <h2>Profile Page</h2> <div> <p>Your account details are below:</p> <table> <tr> <td>Username:</td> <td>{{ account['username'] }}</td> </tr> <tr> <td>Password:</td> <td>{{ account['password'] }}</td> </tr> <tr> <td>Email:</td> <td>{{ account['email'] }}</td> </tr> </table> </div> {% endblock %}