zoukankan      html  css  js  c++  java
  • 27、Flask实战第27天:cms后台登录

    cms后台登录界面

    后台登录页面,我们不用自己写,只需要去Bootstrap中文网去找一个模板改一下就行

    这里使用的模板是:https://v3.bootcss.com/examples/signin/

    点击右键查看网页源码,把源码复制下载

    在项目templates目录下新建目录cms

    在cms目录下新建文件cms_login.html,并把源码复制到该文件中

    cms_login.html会用到样式文件signin.css, 点击它查看源码

    在项目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, 修改后如下

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

    ...
    
    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']中

    在登录装饰器中也用到

    这样做有几个缺点:

      1、当我们fornt也用到session的时候也这样用user_id 就不好区分

      2、因为多处都用这个这个值,很容易写错

    所以,我们可以把它写成一个常量来用,编辑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>
  • 相关阅读:
    7.16PHP所学知识总结
    7.13PHP所学知识总结
    7.11PHP所学知识总结
    2018.08.07jQuery实现焦点轮播图
    2018.07.30js实现轮播图
    2018.07.27javaScript中的DOM操作
    2018.07.23冒泡排序
    2018.07.22 数组
    2018.07.20break和continue的区别
    2018.07.18if条件语句swich条件语句
  • 原文地址:https://www.cnblogs.com/sellsa/p/9426220.html
Copyright © 2011-2022 走看看