iT邦幫忙

2024 iThome 鐵人賽

DAY 30
0
Python

一些Python可以做的事系列 第 30

[Python] 使用 Python, Flask, MySQL 製作登入系統

  • 分享至 

  • xImage
  •  

今天我會根據這篇 ' Building a Login System with Python Flask and MySQL for Beginners ' 去學習如何利用 FLASK 去製作一個登入系統 ,我主要學習系統製作,所以css等樣式我就直接使用這篇的樣本。

基本架構

pythonlogin/
├── static/
│   ├── style.css
├── templates/
│   ├── index.html
│   ├── register.html
│   ├── home.html
│   ├── profile.html
│   └── layout.html      
└── app.py

整體流程

  1. 登入頁面:用戶首先進入登入頁面,輸入帳號和密碼,提交表單進行登入。
  2. 驗證用戶:後端檢查用戶的登入資料,若驗證通過,則進入主頁,否則返回登入頁面並顯示錯誤訊息。
  3. 註冊流程:未註冊的用戶可以通過註冊頁面註冊新帳戶,成功後可以登入系統。
  4. 主頁:已登入的用戶可以訪問主頁,未登入的用戶則會被重定向到登入頁面。
  5. 個人資料:已登入的用戶可以查看其個人資料頁面。
  6. 登出流程:用戶可以選擇登出,登出後會話資料將被清除,並重定向至登入頁面。

建立資料庫並設定 TABLE

首先,在 MySQL Workbench 建立好一個 TABLE (accounts),並新增一份資料以做測試

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');

https://ithelp.ithome.com.tw/upload/images/20240902/20168345e3Vb857xrJ.png

建立登入系統 app.py

from flask import Flask, render_template, request, redirect, url_for, session
from flask_mysqldb import MySQL
import MySQLdb.cursors
import MySQLdb.cursors, re, hashlib

app = Flask(__name__)

# 設定這個為您的密鑰(可以是任何東西,用於額外保護)
app.secret_key = 'yoursecretkey'

# 輸入您的資料庫連接詳細信息
app.config['MYSQL_HOST'] = 'localhost'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = 'password'
app.config['MYSQL_DB'] = 'pythonlogin'

# 初始化 MySQL
mysql = MySQL(app)

# 登入頁面 - http://localhost:5000/pythinlogin/ 
@app.route('/pythonlogin/', methods=['GET', 'POST'])
def login():
    # 如果出現問題,輸出一條消息...
    msg = ''
    # 檢查 POST 請求中是否存在 "username" 和 "password"(用戶提交了表單)
    if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
        # 創建變量以便於訪問
        username = request.form['username']
        password = request.form['password']
        # 使用 MySQL 檢查帳戶是否存在
        cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
        cursor.execute('SELECT * FROM accounts WHERE username = %s AND password = %s', (username, password,))
        # 獲取一條記錄並返回結果
        account = cursor.fetchone()
        # 如果帳戶存在於資料庫中的 accounts 表
        if account:
            # 創建會話數據,我們可以在其他路由中訪問這些數據
            session['loggedin'] = True
            session['id'] = account['id']
            session['username'] = account['username']
            # 重定向到主頁
            return redirect(url_for('home'))
        else:
            # 帳戶不存在或用戶名/密碼不正確
            msg = '用戶名或密碼不正確!'
    # 顯示登錄表單並顯示消息(如果有)
    return render_template('index.html', msg=msg)

# 首頁,只能由已登錄的用戶訪問 - http://localhost:5000/pythinlogin/home 
@app.route('/pythonlogin/home')
def home():
    # 檢查用戶是否已登錄
    if 'loggedin' in session:
        # 用戶已登錄,顯示主頁
        return render_template('home.html', username=session['username'])
    # 用戶未登錄,重定向到登錄頁面
    return redirect(url_for('login'))

# 登出頁面 - http://localhost:5000/python/logout 
@app.route('/pythonlogin/logout')
def logout():
    # 移除會話資料,這將使用戶登出
   session.pop('loggedin', None)
   session.pop('id', None)
   session.pop('username', None)
   # 重定向到登錄頁面
   return redirect(url_for('login'))

# 註冊頁面 - http://localhost:5000/pythinlogin/register 
# 需要使用 GET 和 POST 請求
@app.route('/pythonlogin/register', methods=['GET', 'POST'])
def register():
    # 如果出現問題,輸出一條消息...
    msg = ''
    # 檢查 POST 請求中是否存在 "username", "password" 和 "email"(用戶提交了表單)
    if request.method == 'POST' and 'username' in request.form and 'password' in request.form and 'email' in request.form:
        # 創建變量以便於訪問
        username = request.form['username']
        password = request.form['password']
        email = request.form['email']
        # 使用 MySQL 檢查帳戶是否存在
        cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
        cursor.execute('SELECT * FROM accounts WHERE username = %s', (username))
        account = cursor.fetchone()
        # 如果帳戶存在,顯示錯誤並進行驗證檢查
        if account:
            msg = '帳戶已存在!'
        elif not re.match(r'[^@]+@[^@]+\.[^@]+', email):
            msg = '無效的電子郵件地址!'
        elif not re.match(r'[A-Za-z0-9]+', username):
            msg = '用戶名只能包含字母和數字!'
        elif not username or not password or not email:
            msg = '請填寫表單!'
        else:
            # 帳戶不存在且表單數據有效,現在將新帳戶插入到 accounts 表中
            cursor.execute('INSERT INTO accounts VALUES (NULL, %s, %s, %s)', (username, password, email))
            mysql.connection.commit()
            msg = '您已成功註冊!'
    elif request.method == 'POST':
        # 表單為空...(沒有 POST 數據)
        msg = '請填寫表單!'
    # 顯示註冊表單並顯示消息(如果有)
    return render_template('register.html', msg=msg)

# 個人資料頁面 - http://localhost:5000/pythinlogin/profile 
# 只能由已登錄的用戶訪問
@app.route('/pythonlogin/profile')
def profile():
    # 檢查用戶是否已登錄
    if 'loggedin' in session:
        # 我們需要用戶的所有帳戶信息,以便在個人資料頁面上顯示
        cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
        cursor.execute('SELECT * FROM accounts WHERE id = %s', [session['id']])
        account = cursor.fetchone()
        # 顯示帶有帳戶信息的個人資料頁面
        return render_template('profile.html', account=account)
    # 用戶未登錄,重定向到登錄頁面
    return redirect(url_for('login'))

if __name__ == "__main__":
    app.run(debug=True)

製作 index.html 作為登入頁面

建立了包含輸入欄位的表單:使用者名稱和密碼,表單的方法設定為post,這將使用 POST 請求將表單資料傳送到我們的 Python Flask 伺服器

<!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="{{ url_for('register') }}">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>

建立註冊模板 register.html

表單的action屬性設定為「register」路由,我們將使用該路由來處理POST請求

<!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>

為登入頁面建立佈局,編輯layout.html檔案

在主頁(home.html)和個人資料(profile.html)頁面上使用相同的佈局

<!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>

建立主頁 home.html

使用者登入後的主頁

{% extends 'layout.html' %}

{% block title %}Home{% endblock %}

{% block content %}
<h2>Home Page</h2>
<p>Welcome back, {{ username }}!</p>
{% endblock %}

建立個人資料頁面 profile.html

使用者可以在個人資料頁面查看其詳細資訊(使用者名稱、密碼和電子郵件)

{% 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 %}

style.css (直接參考資料來源)

參考資料 :
https://morioh.com/a/c61187faa9be/building-a-login-system-with-python-flask-and-mysql-for-beginners
Python Flask Authentication Tutorial - Learn Flask Login


上一篇
[Python] 連線MySQL資料庫
系列文
一些Python可以做的事30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言