2七、Flask實戰第27天:cms後臺登陸

cms後臺登陸界面

後臺登陸頁面,咱們不用本身寫,只須要去Bootstrap中文網去找一個模板改一下就行css

這裏使用的模板是:https://v3.bootcss.com/examples/signin/html

點擊右鍵查看網頁源碼,把源碼複製下載前端

在項目templates目錄下新建目錄cmsweb

在cms目錄下新建文件cms_login.html,並把源碼複製到該文件中flask

cms_login.html會用到樣式文件signin.css, 點擊它查看源碼bootstrap

在項目static目錄下新建目錄 cms/css瀏覽器

在static/cms/css新建文件signin.css, 並把源碼拷貝進去服務器

body {
  padding-top: 40px;
  padding-bottom: 40px;
  background-color: #eee;
}

.form-signin {
  max-width: 330px;
  padding: 15px;
  margin: 0 auto;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
  margin-bottom: 10px;
}
.form-signin .checkbox {
  font-weight: normal;
}
.form-signin .form-control {
  position: relative;
  height: auto;
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
  padding: 10px;
  font-size: 16px;
}
.form-signin .form-control:focus {
  z-index: 2;
}
.form-signin input[type="email"] {
  margin-bottom: -1px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
  margin-bottom: 10px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
signin.css

修改cms_login.html, 修改後以下cookie

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3個meta標籤*必須*放在最前面,任何其餘內容都*必須*跟隨其後! -->
    <meta name="description" content="">
    <meta name="author" content="">

    <title>登陸-論壇CMS管理系統</title>

    <!-- Bootstrap core CSS -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">


    <!-- Custom styles for this template -->
    <link href="{{ url_for('static', filename='cms/css/signin.css') }} " rel="stylesheet">

  </head>

  <body>

    <div class="container">

      <form class="form-signin" method="post">
        <h2 class="form-signin-heading">請登陸</h2>
        <label for="inputEmail" class="sr-only">郵箱</label>
        <input type="email" id="inputEmail" class="form-control" placeholder="郵箱" required autofocus name="email">
        <label for="inputPassword" class="sr-only">密碼</label>
        <input type="password" id="inputPassword" class="form-control" placeholder="密碼" required name="password">
        <div class="checkbox">
          <label>
            <input type="checkbox" value="1" name="remember"> 記住我
          </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">登陸</button>
      </form>

    </div> <!-- /container -->

  </body>
</html>
cms_login.html

編寫cms登陸視圖,編輯cms.views.pysession

...

from flask views, render_template

class LoginView(views.MethodView):
    def get(self):
        return render_template('cms/cms_login.html')

    def post(self):
        pass

bp.add_url_rule('/login/', view_func=LoginView.as_view('login'))

啓動訪問http://127.0.0.1:5000/cms/login/

 

cms後臺登陸功能

後臺登陸,前端就須要提交數據給咱們,那麼首先,咱們就須要form驗證

安裝flask-wtf

pip install flask-wtf

編輯cms.forms.py

from wtforms import Form, StringField, IntegerField
from wtforms.validators import Email, InputRequired, Length

class LoginForm(Form):
    email = StringField(validators=[Email(message='郵箱格式錯誤'), InputRequired(message='請輸入郵箱')])
    password = StringField(validators=[Length(6,20, message='密碼長度爲6-20位')])
    remember = IntegerField()  #這個值不用驗證,這裏只是接收
cms.forms.py

而後就能夠寫視圖了,編輯cms.views.py

from flask import Blueprint, views, render_template, request, session
from flask import redirect, url_for
from .forms import LoginForm
from .models import CMSUser

bp = Blueprint('cms', __name__, url_prefix='/cms')

@bp.route('/')
def index():
    return 'cms index'

class LoginView(views.MethodView):
    def get(self, message=None):
        return render_template('cms/cms_login.html', message=message)

    def post(self):
        login_form = LoginForm(request.form)
        if login_form.validate():
            email = login_form.email.data
            password = login_form.password.data
            remember = login_form.remember.data
            user = CMSUser.query.filter_by(email=email).first()
            if user and user.check_password(password):
                session['user_id'] = user.id
                if remember:
                    #若是勾選了記住我,則保存session,這樣就算瀏覽器關閉session仍是存在的
                    session.permanent = True
                return redirect(url_for('cms.index'))  #由於是藍圖這裏必須使用cms.index,不能使用index
            else:
                #return render_template('cms/cms_login.html', message='帳號或密碼錯誤')
                #等同於如下代碼
                return self.get(message='帳號或密碼錯誤')

        else:
            #login_form.errors是一個字典,如{"email":['郵箱格式錯誤'], "password":["密碼長度爲6-20位"]}
            #login_form.errors.popitem() 是取出字典的任意一項,結果是元組,如:("password":["密碼長度爲6-20位"])
            #取出該元組的第2個元素:login_form.errors.popitem()[1], 如:["密碼長度爲6-20位"]
            #最後取出錯誤提示語:login_form.errors.popitem()[1][0]
            message = login_form.errors.popitem()[1][0]
            return self.get(message=message)


bp.add_url_rule('/login/', view_func=LoginView.as_view('login'))
cms.views.py

由於用到了session,因此還須要在配置文件,配置一個key爲session加密用

...
import os
from datatime import timedelta

#session
SECRET_KEY = os.urandom(24)
#設置session有效期爲2天,若開啓了session.permanent後不設置該參數,則默認爲31天
PERMANENT_SESSION_LIFETIME = timedelta(days=7)
config.py

前端代碼須要加上錯誤提示,首先要判斷message是否存在

 

訪問輸入正確的郵箱,就能夠登陸成功,跳轉的http://127.0.0.1/cms/而且保存用戶session到cookie了

 

CMS後臺登陸限制

限制存在個問題,就是咱們沒有登陸的狀況下,也可以訪問cm後臺的首頁http://127.0.0.1/cms/.接下來咱們就要給它加上限制,只有登陸後才能訪問。

要實現這個功能,可使用裝飾器

首先在cms下面新建個文件decorators.py, 之後此藍圖下的全部裝飾器都放到這個文件裏面

from flask import session, redirect, url_for
from functools import wraps

def login_required(func):
    @wraps(func) # 保留func的屬性
    def inner(*args, **kwargs):
        if 'user_id' in session:
            return func(*args, **kwargs)
        else:
            #session中沒有user_id表示沒有登陸,則跳轉到登陸頁面
            return redirect(url_for('cms.login'))
    return inner
decorators.py

而後在cms首頁視圖加上login_required裝飾器

...

from .decorators import login_required

@bp.route('/')   #路由裝飾器必定要放在最上面,不然會找不到路由
@login_required 
def index():
    return 'cms index'
cms.views.py

這樣,當咱們在沒有登陸的狀況下訪問http://127.0.0.1/cms/會自動跳轉到登陸頁面了,O(∩_∩)O哈哈~

 

代碼優化

優化一

能夠發現,咱們把用戶的session信息(user.id)保存在session['user_id']中

在登陸裝飾器中也用到

這樣作有幾個缺點:

  一、當咱們fornt也用到session的時候也這樣用user_id 就很差區分

  二、由於多處都用這個這個值,很容易寫錯

因此,咱們能夠把它寫成一個常量來用,編輯config.py

...
CMS_USER_ID = 'HEBOANHEHE'

而後在用到user_id的地方都import config ,而後使用CMS_USER_ID

 

優化二

 當表單驗證不經過之後,咱們是以下實現的,隨機抽取一個錯誤信息出來(實際項目中可能要列出全部的錯誤信息,要根據需求來定)

message = login_form.errors.popitem()[1][0]

咱們能夠把獲取錯誤信息的代碼寫到 form中,這樣就避免每次都寫這麼長的代碼,也避免容易犯錯

class LoginFrom(Form):
    email = StringField(validators=[Email(message='郵箱格式錯誤'),InputRequired(message='請輸入郵箱') ])
    password = StringField(validators=[Length(6,30, message='密碼長度6-30')])
    remember = IntegerField()
    
    #添加獲取錯誤信息的方法
    def get_error(self):
        message = self.errors.popitem()[1][0]
        return message

而後咱們視圖中調用這個方法就能夠了

message = login_form.get_error()

由於項目中之後還會有其餘的form驗證,那麼每一個form中都要加這個方法,這樣也比較麻煩,因此,咱們能夠進一步優化。咱們可使用繼承來解決

在apps/下面新建一個forms.py

from wtforms import Form

class BaseForm(Form):
    def get_error(self):
        message = self.errors.popitem()[1][0]
        return message

之後咱們寫的form繼承BaseForm就擁有get_error方法了

class LoginFrom(BaseForm):
    email = StringField(validators=[Email(message='郵箱格式錯誤'),InputRequired(message='請輸入郵箱') ])
    password = StringField(validators=[Length(6,30, message='密碼長度6-30')])
    remember = IntegerField()

 

CSRF保護

編輯主程序文件bbs.py,開啓csrf

 ...
from flask_wtf import CSRFProtect

CSRFProtect(app)

加上csrf保護後,咱們再嘗試登錄就會失敗

由於還需在提交表單的 form中添加一個input攜帶csrf_token給服務器驗證

<form class="form-signin" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
...

</form>
相關文章
相關標籤/搜索