# Flask實作_使用者登入功能_03_使用者登入_完成login檢核 ###### tags: `python` `flask` `flask-login` :::danger 前置閱讀: * [Flask-Login](https://hackmd.io/s/ryvr_ly8f) ::: ## 說明 打好前面的基礎,我們要正式的進入設置login的功能,透過流程圖讓自己更了解需求的工作項目,為了避免一次調整過多,login的部份會分成兩個章節來說明。 我沒有進入過軟體設計公司,因此並不知道他們在需求書上的格式,只是我都會習慣自己先規劃好需求功能清單之後再來作業,不管如何,想好再動手會比邊做邊動手來的好。 在建置功能的時候,習慣上會從Model先處理,有Model,表單就有參照,表單有了就可以設置Html,最後再加上View Function、Route,當然如果有初始化以及參數設置需求的話當然是放第一。 ### 流程圖 ```flow st=>start: 流程開始 e=>end: 結束 cond1=>condition: 帳號檢驗 cond2=>condition: 密碼檢驗 op0=>operation: 登入網頁 op1=>operation: 按下登入 io1=>inputoutput: 輸入帳號 io2=>inputoutput: 輸入密碼 st->op0->io1->io1->op1->cond1 cond1(no)->op0 cond1(yes)->cond2 cond2(no)->op0 cond2(yes)->e ``` ### 工作清單 1. function: login * View Function: `login` * 加入密碼驗證功能 * 登入成功之後引導使用者到`next`或指定頁面 * Route: `author/login` * Model: `UserRegister` * 加入密碼驗證功能 * 繼承`flask-login` * 設置`flask-login`的`load_user` * Form: `LoginForm` * 表單需有『記住我』的功能 * Template: `author\login.html` * remark: * 登入功能 2. function: logout * View Function: `logout` * 利用`flask-login`實作`logout` * Route: `author/logout` * remark: * 登出功能 3. other * `config.py`加入參數 * 加入`flask-login`初始化 * 測試`flask-login`的裝飾器`login_required` ## 作業說明 ### other 實作`login`的部份會應用`flask-login`,絕大部份都需要先初始化,因此務必先初始化`flask-login`,後面才有辦法正常使用功能。 :::success * 文件:`app\__init__.py` * 說明:初始化`flask-login` ```python= # 追加import flask-login from flask_login import LoginManager #...中略...# login = LoginManager(app) login.login_view = 'login' ``` 第5行:這邊設置的是使用者登入功能所在的`endpoint`,通常為路由名稱,此例為`login`,只要使用者不是透過`login`來開啟網頁而該網頁有`login_required`的時候,就會將使用者引導至此。<sub>如下說明:</sub> ``` #: The name of the view to redirect to when the user needs to log in. #: (This can be an absolute URL as well, if your authentication #: machinery is external to your application.) ``` ::: 接著調整系統參數,如下設置: :::success * 文件:`config.py` * 說明:設置`flask-login`中對`session`的安全等級設置 ```python= SESSION_PROTECTION = 'strong' ``` ::: ### MODEL 在建置註冊功能的時候,我們透過`flask_bcrypt`將使用者密碼加密保存,現在我們要產生一個解密的驗證,如果正確就讓使用者登入,如果不正確就產生一個`flash-message`給使用者,並且告知帳號或密碼錯誤。 為什麼是帳號或密碼錯誤?如果是駭客在測帳號,而我們告知密碼錯誤,那駭客在最低程度上就知道帳號對了,當然這部份如果在公司內部的話可能可以有所調整,不然搞死人搞到最後還是搞到自己人。 :::success * 文件:`app_blog\author\model.py` * 說明:調整`UserRegister`,加入method`check_password`驗證密碼 ```python= class UserRegister(db.Model) #...中略...# def check_password(self, password): """ 密碼驗證,驗證使用者輸入的密碼跟資料庫內的加密密碼是否相符 :param password: 使用者輸入的密碼 :return: True/False """ return bcrypt.check_password_hash(self.password_hash, password) ``` 第9行:驗證密碼,目前尚未綁定`flask-login`,會於下一話再加入設置 ::: ### FORM 我們設置的需求清單中,表單必需有『記得我』的功能,這會搭配`flask-login`的功能來實現。 :::success * 文件:`app_blog\author\form.py` * 說明:建置使用者登入表單 ```python= # 追加import from wtforms import BooleanField class FormLogin(FlaskForm): """ 使用者登入使用 以email為主要登入帳號,密碼需做解碼驗證 記住我的部份透過flask-login來實現 """ email = EmailField('Email', validators=[ validators.DataRequired(), validators.Length(5, 30), validators.Email() ]) password = PasswordField('PassWord', validators=[ validators.DataRequired() ]) remember_me = BooleanField('Keep Logged in') submit = SubmitField('Log in') ``` 記得養成邊調整邊測試的好習慣,在完成表單之後,就可以快速的利用`register.html`來複製成`login.html`,並且測試目前的表單是否可以正常渲染,如下圖: ![](https://i.imgur.com/I5u4nIS.png) ::: ### Html 在上面為了確認表單是否正常的時候已經有複製一個`login.html`,現在我們來簡單調整以符合需求,如下: :::success * 文件:`template\author\login.html` * 說明:調整登入頁面 ```htmlmixed= {% extends "base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% block title %}Login Page{% endblock %} {% block page_content %} <div class="page-header"> <h1>Login</h1> <h2>Welcome To My Blog</h2> </div> <div class="col-md-4"> {{ wtf.quick_form(form) }} </div> {% endblock %} ``` ::: ### View Function 在Model、Form、Html都設置好之後就可以到View Function這邊整合,預計加入使用者登入密碼的驗證,下一話才會再加入將使用者引導回上一個連結的功能。 :::success * 文件:`app_blog\author\view.py` * 說明:加入使用者登入密碼驗證 ```python # import稍早新增的FormLogin from app_blog.author.form import FormRegister, FormLogin # flask追加import redirect, url_for from flask import render_template, flash, redirect, url_for, request @app.route('/login', methods=['GET', 'POST']) def login(): form = FormLogin() if form.validate_on_submit(): # 當使用者按下login之後,先檢核帳號是否存在系統內。 user = UserRegister.query.filter_by(email=form.email.data).first() if user: # 當使用者存在資料庫內再核對密碼是否正確。 if user.check_password(form.password.data): return 'Welcome' else: # 如果密碼驗證錯誤,就顯示錯誤訊息。 flash('Wrong Email or Password') else: # 如果資料庫無此帳號,就顯示錯誤訊息。 flash('Wrong Email or Password') return render_template('author/login.html', form=form) ``` ::: 最後,我們來測試是否可以正確的補捉到密碼錯誤,如下: :::warning * 測試 * 測試說明:測試登入失敗的狀況 * 測試連結:`http://127.0.0.1:5000/login` ![](https://i.imgur.com/YHNXR9Z.png) ![](https://i.imgur.com/jXOkOd3.png) ::: ## 總結 這一話我們初始化`flask-login`,但還沒有真正的用到,下一話會補足其它功能,要特別注意到`login.login_view = 'login'`的部份,在未來加入`blueprint`之後必需加入`blueprint`的前綴,當然後續我們還是會提到。 **上一話:**[Flask實作_使用者登入功能_02_調整register頁面](https://hackmd.io/s/BJM_IAzC7) **下一話:**[Flask實作_使用者登入功能_04_使用者登入_結合flask-login](https://hackmd.io/s/HyIArp-wG)