跳至主要內容

Max行銷誌

行銷、數據分析、與 Python

  • Python
  • Git
  • Flask
  • Crawler
  • GTM
  • Data Analytics
  • Dashboard
  • About

Max行銷誌

行銷、數據分析、與 Python

  • Python
  • Git
  • Flask
  • Crawler
  • GTM
  • Data Analytics
  • Dashboard
  • About

Max行銷誌

行銷、數據分析、與 Python

【Flask教學系列】實作 Flask Session-base login 登入驗證

By MaxAll posts、Python Flask 教學 最後更新時間 23 9 月, 2020
Flask教學_實作Flask-login驗證_Max行銷誌

本篇介紹 Session-base login 登入驗證,完整範例存放於 template-flask-login · GitHub,歡迎 Git Clone 使用~

目錄

  • ㄧ. 環境設置
    • 1. 安裝套件
    • 2. 了解整體架構
    • 3. 了解 route 路徑
  • 二. 進入正題 – 實作 Flask Login 驗證
    • 1. 設定 註冊會員頁
    • 2. 設定 model
    • 3. 驗證使用者登入狀態
  • 三. 淺談 Session-based 驗證機制:

ㄧ. 環境設置

1. 安裝套件

使用 pip3 install -r requirements.txt 來安裝此次會需要的套件。

▍本次使用的 Flask 擴充套件清單如下:

  1. 資料庫使用 Flask-SQLAlchemy ORM 操作和連線,設定連線的資料庫是 SQLite
  2. 接收到 request 請求的序列化和驗證資料使用 Marshmallow 套件
  3. RESTful API 使用 Flask-RESTful 套件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# requirements.txt
 
aniso8601==8.0.0
Click==7.0
Flask==1.1.1
Flask-RESTful==0.3.8
Flask-SQLAlchemy==2.4.1
itsdangerous==1.1.0
Jinja2==2.11.1
MarkupSafe==1.1.1
marshmallow==3.6.0
PyMySQL==0.9.3
python-dotenv==0.13.0
pytz==2020.1
six==1.14.0
SQLAlchemy==1.3.17
Werkzeug==1.0.0

2. 了解整體架構

架構上使用 Flask Application Factories (工廠模式),和 MVC (Model–view–controller)

  • Model – 負責資料庫操作和儲存。
  • View (Flask 內稱為 Templates) – 負責使用者介面設計。
  • Controller (Flask 內稱為 View) – 負責對 Request / Response 處理,和負責與 Model 的資料溝通,並將資料串接到 View (Templates)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├── app
│   ├── __init__.py
│   ├── config
│   │   ├── config.py
│   │   └── test.db
│   ├── model
│   │   └── user.py
│   ├── templates
│   │   ├── login.html
│   │   └── signup.html
│   └── view
│       ├── abort_msg.py
│       └── auth.py
├── main.py
└── requirements.txt

3. 了解 route 路徑

▍確認環境設立成功:

在終端機執行 flask run --reload

  1. 測試連線 http://127.0.0.1:5000/,如果得到 success 回應則代表套件都安裝完成!
  2. 連線 http://127.0.0.1:5000/create_all,建立此次需要的 sqlite 測試資料庫,會被存放在 /app/config/test.db 位置。

▍其他相關路徑:

  • 建立帳號:http://127.0.0.1:5000/auth/singup
  • 登入帳號:http://127.0.0.1:5000/auth/login
  • 登出帳號:http://127.0.0.1:5000/auth/logout
  • 測試限制 member 權限網址: http://127.0.0.1:5000/normal_member
  • 測試限制 admin 權限網址: http://127.0.0.1:5000/admin_member

二. 進入正題 – 實作 Flask Login 驗證

此次只會以會員註冊頁為範例講解,完整範例存放於 template-flask-login · GitHub,歡迎 Git Clone 使用~

1. 設定 註冊會員頁

位於 app/view/auth.py,Signup 完整程式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Signup(Resource):
    def post(self):
        try:
            # 資料驗證
            user_data = users_schema.load(request.form, partial=True)
            # 註冊
            new_user = UserModel(user_data)
            new_user.save_db()
            new_user.save_session()
            return {'msg': 'registration success'}, 200
 
        except ValidationError as error:
            return {'errors': error.messages}, 400
 
        except Exception as e:
            return {'errors': abort_msg(e)}, 500
 
    def get(self):
        return make_response(render_template('signup.html'))
 
api.add_resource(Signup, '/signup')

首先我們看到第五行,我們使用 request.form 接收前端 Form 表單傳過來的使用者帳號和密碼,並且使用 Flask 擴充套件 Marshmallow 的 users_schema.load 語法進行資料驗證。

接下來可以看到第七行,將驗證過後的帳號密碼放入 UserModel(user_data) ,實例化新的使用者後存入 db new_user.save_db() 和設定使用者的 session new_user.save_session()。

1
2
3
new_user = UserModel(user_data) # 實例化新使用者
new_user.save_db() # 將新使用者存入 db
new_user.save_session() # 設定新使用者的 session

如果是資料驗證錯誤 (密碼少於 6 碼、或缺少帳號/密碼欄位) 則會在 except ValidationError as error: 這邊觸發。

1
2
except ValidationError as error:
    return {'errors': error.messages}, 400

而如果是其他錯誤訊息則會在這邊觸發 except Exception as e:,並且將錯誤訊息清理過後回覆給前端 {'errors': abort_msg(e)}

1
2
except Exception as e:
    return {'errors': abort_msg(e)}, 500

沒有發生錯誤,則結束並返回註冊成功 {'msg': 'registration success'} 訊息。

2. 設定 model

看到這邊大家心裡應該有不少問題,像是使用者密碼的 hash 處理?或資料驗證做了哪些事情?或存入 Session 都存了些什麼資料?

以上這些都將會在 model 裡面為大家講解,先來看一下 app/mdoel/user.py 的完整代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 
class UserModel(db.Model):
    __tablename__ = 'user'
    uid = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(30), unique=True)
    password_hash = db.Column(db.String(255))
    role = db.Column(db.String(10), default='normal')
    insert_time = db.Column(db.DateTime, default=datetime.now)
    update_time = db.Column(db.DateTime,
                            onupdate=datetime.now,
                            default=datetime.now)
 
    def __init__(self, user_data):
        self.name = user_data['name']
        self.password = user_data['password']
 
    @property
    def password(self):
        raise AttributeError('passowrd is not readabilty attribute')
 
    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)
 
    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)
 
    @classmethod
    def get_user(cls, name):
        return cls.query.filter_by(name=name).first()
 
    def save_db(self):
        db.session.add(self)
        db.session.commit()
 
    def save_session(self):
        session['username'] = self.name
        session['role'] = self.role
        session['uid'] = self.uid
 
    @staticmethod
    def remove_session():
        session['username'] = ''
        session['role'] = ''
        session['uid'] = ''
 
 
class UserSchema(Schema):
    uid = fields.Integer(dump_only=True)
    name = fields.String(required=True, validate=validate.Length(3))
    password = fields.String(required=True, validate=validate.Length(6))
    role = fields.String()
    insert_time = fields.DateTime()
    update_time = fields.DateTime()

▍使用者密碼加密處理

可以看到第 16 行 @password.setter 這邊,如果要對 password 賦值的話,就會被 generate_password_hash(password) 加密,並且產生新的屬性 self.password_hash 再存入資料庫裡面。

回到剛剛 view 裡面 UserModel(user_data) 的這段實例化新使用者程式,在 Model 裡面已經做了加密的處理。

而如果對 view 裡面實例化後的新使用者,再次呼叫 password 的屬性話,則會產生 “passowrd is not readabilty attribute” 的錯誤訊息!要拿到 password 只能呼叫被加密過的密碼 password_hash。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class UserModel(db.Model):
    __tablename__ = 'user'
    uid = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(30), unique=True)
    password_hash = db.Column(db.String(255))
    role = db.Column(db.String(10), default='normal')
    insert_time = db.Column(db.DateTime, default=datetime.now)
    update_time = db.Column(db.DateTime,
                            onupdate=datetime.now,
                            default=datetime.now)
 
    def __init__(self, user_data):
        self.name = user_data['name']
        self.password = user_data['password']
 
    @property
    def password(self):
        raise AttributeError('passowrd is not readabilty attribute')
 
    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)
 
    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)
 

▍傳入資料驗證

我們在 model/user.py 內創建了 UserSchema 的類別並且繼承了 marshmallow 套件的 Schema,並且在此類別內制定好了欄位的名稱以及驗證內容,像是欄位 password 必須是 fields.String 型態,且輸入長度需要大於 6 validate.Length(6)

所以在 view 裡面 users_schema.load(request.form, partial=True) 只需呼叫 load 就可以很輕鬆地進行資料驗證了!

1
2
3
4
5
6
7
8
9
from marshmallow import Schema, fields, pre_load, validate
 
class UserSchema(Schema):
    uid = fields.Integer(dump_only=True)
    name = fields.String(required=True, validate=validate.Length(3))
    password = fields.String(required=True, validate=validate.Length(6))
    role = fields.String()
    insert_time = fields.DateTime()
    update_time = fields.DateTime()

▍Session 都存了些什麼資料

我們在 Session 中儲存了使用者的 username 和 role 以及 uid,而如果登出後只需要呼叫 remove_session() 就會將剛剛設定的 Session 清空囉!

1
2
3
4
5
6
7
8
9
10
    def save_session(self):
        session['username'] = self.name
        session['role'] = self.role
        session['uid'] = self.uid
 
    @staticmethod
    def remove_session():
        session['username'] = ''
        session['role'] = ''
        session['uid'] = ''

3. 驗證使用者登入狀態

1
2
3
4
@app.route('/normal_member')
@check_login('normal')
def member_normal_page():
    return 'ok'

如果要像上面第二行 @check_login 可以使用裝飾詞來判斷是否登入的話,我們需要寫一個裝飾詞 。

首先會先去檢查使用者 Session 內有沒有 role session.get('role'),如果沒有 role 則噴 401 權限不足錯誤訊息。

有 role 的話,則判斷裝飾詞傳入的 check_role,與 Session 內的 role 是否有資格登入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def check_login(check_role):
    def decorator(func):
        def wrap(*args, **kw):
            user_role = session.get('role')
 
            if user_role == None or user_role == '':
                return abort(401)
            else:
                if check_role == 'admin' and check_role == user_role:
                    return func(*args, **kw)
                if check_role == 'normal':
                    return func(*args, **kw)
                else:
                    return abort(401)
 
        wrap.__name__ = func.__name__
        return wrap
 
    return decorator

設定好後執行 flask run --reload

  • 建立帳號:http://127.0.0.1:5000/auth/singup
  • 測試限制 member 權限網址: http://127.0.0.1:5000/normal_member
  • 測試限制 admin 權限網址: http://127.0.0.1:5000/admin_member

如果都沒有噴錯就代表成功囉!

三. 淺談 Session-based 驗證機制:

Session-based Authentication 認證機制是將使用者的 Session Information 存放在 Cookie 中,利用 HTTP request 和 response 所攜帶的 Cookie 做為使用者身份驗證的機制,也就是說 Server 端和 Client 端都必須儲存狀態資訊 (例如 Server 端必須將使用者資料存在 Session database 或 memory 中,而 Client 端也必須用 Cookie 儲存 Session Information),因此在使用 Session-based Authentication 通常會有以下缺點:

  1. 由於 Client 使用 Cookie 存放 Session Information,會需要處理 CSRF 攻擊 的防護。
  2. 當 Server 需要擴展 scalability 時,例如後端 Server 要從一台擴充成三台,需要煩惱每台之間的 Session 問題。
  3. Server Side Session 的使用情境下,當使用者每次發送請求時,都要使用 session_id 與資料庫交換資料,當同時使用者過多時,會佔據大量的伺服器資源。

下一篇我們將介紹 Token-based Authentication:【Flask教學】 Flask-JWT-Extended 實作

最後~

▍回顧本篇我們介紹了的 Flask login 內容:

  • 環境設置
    1. 安裝套件
    2. 了解整體架構
    3. 了解 route 路徑
  • 進入正題 – 實作 Flask Login 驗證機制
    1. 設定 註冊會員頁
    2. 設定 model
    3. 驗證使用者登入狀態
  • 最後 – 淺談 Session-based 驗證機制優缺

關於 Flask 教學的延伸閱讀:

▍關於 Flask 教學系列目錄:

  • 【Flask教學系列】 實作 Flask 教學目錄

▍其他 Flask 相關教學:

  • 【Flask教學系列】Flask 為甚麼需要 WSGI 與 Nginx
  • 【Flask教學系列】實作 Flask CORS
  • 【Flask教學系列】實作 Flask CSRF Protection
  • 【Flask教學系列】實作 Dockerfile + nginx + ssl + Flask 教學 (附GitHub完整程式)

那 【Flask教學系列】實作 Flask Session-base login登入驗證機制 的介紹就到這邊告一個段落囉!有任何問題可以在以下留言~

有關 Max行銷誌的最新文章,都會發佈在 Max 的 Facebook 粉絲專頁,如果想看最新更新,還請您按讚或是追蹤唷!

flaskflask loginflask sessionflask session base loginflask 登入驗證flask教學

文章導覽

【Flask教學系列】實作 Flask 序列化 和 反序列化 方法
【資料庫筆記】 PostgreSQL 基礎教學和練習題操作

Archives

  • All posts
  • 2026
  • 2025 (8 pages)
  • 2024 (4 pages)
  • 2023 (22 pages)
  • 2022 (20 pages)
  • 2021 (17 pages)
  • 2020 (48 pages)
  • 2019 (35 pages)
  • 2018 (31 pages)

Category

  • Python 從 0 開始教學手冊
  • Git 學習路線指南
  • Flask 入門指南
  • 進階爬蟲
  • 走在數據工程師的路上
  • 數據視覺化 Looker Studio
  • 數據追蹤學 GTM

Hot Topic

  • 蝦皮賣家競品分析
  • 電商小細節:頁面優化經驗談
  • 電商數據儀表板
  • 從部落格學數據分析
  • Python 一切皆為物件,到底什麼是物件 Object ?
  • Git 時光機 – 回復版本的 2 種方法
  • 過去一年,下班後的學習

Tags

  • All posts (178)
  • Crypto (6)
  • Git 教學 (10)
  • Google Tag Manager 教學 (19)
  • Looker Studio 教學 (13)
  • n8n (3)
  • Python Django 教學 (3)
  • Python Flask 教學 (36)
  • Python 基礎教學 (29)
  • Python 數據分析 (25)
  • Python 機器學習 (6)
  • Python 爬蟲教學 (15)
  • Python 資料庫教學 (12)
  • Swift 教學 (5)
  • 數據自動化 (1)

About

  • Made with ☕ by Max

About

  • Made with ☕ by Max

Archives

  • All posts
  • 2026
  • 2025 (8 pages)
  • 2024 (4 pages)
  • 2023 (22 pages)
  • 2022 (20 pages)
  • 2021 (17 pages)
  • 2020 (48 pages)
  • 2019 (35 pages)
  • 2018 (31 pages)

Explore

  • Python
  • Git
  • Flask
  • Crawler
  • Data Analytics
  • Dashboard
  • GTM
  • Max Newsletter
© 2026 Max行銷誌.