您现在的位置是: 网站首页 >Flask >Flask搭建微电影视频网站 Flask

【Flask微电影】13.管理员登录、退出、装饰器进行访问控制

admin2018年11月8日 21:46 Flask | Html | Python 816人已围观

Flask搭建微电影视频网站简介 利用Flask搭建微电影视频网站 Github地址:https://github.com/xyliurui/FlaskMovie

## 管理员登录 - `app/__init__.py`中创建db对象(将以前的`app/models.py`的db对象移动过去) - `app/models.py`中导入db对象 - `app/admin/forms.py`中定义表单验证功能,需要出啊关键表单:`LoginForm` - `app/templates/admin/login.html`中使用表单字段、信息验证、消息闪现 - `app/admin/views.py`中login视图处理登录请求,将登陆信息保存会话 - `app/admin.views.py`中增加登录装饰器,然后对其他视图进行访问控制 ### 优化代码结构 将`models.py`中的db对象移动到`app/__init__.py`中 ![BLOG_20181108_214824_84](/media/blog/images/2018/11/BLOG_20181108_214824_84.png "博客图集BLOG_20181108_214824_84.png") **app/\_\_init\_\_.py**中修改 ```python from flask import Flask, render_template from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) # 创建app对象 app.debug = True # 开启调试模式 # app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:root@127.0.0.1:3306/movie" # 定义数据库连接,传入连接,默认端口3306,可不写 app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+mysqlconnector://root:root@127.0.0.1:3306/movie" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True # 定义db对象,实例化SQLAlchemy,传入app对象 db = SQLAlchemy(app) from app.home import home as home_blueprint from app.admin import admin as admin_blueprint # 注册蓝图 app.register_blueprint(home_blueprint) app.register_blueprint(admin_blueprint, url_prefix="/admin") ``` **app/models.py**中导入db对象,原来的app配置就不需要了 ```python from app import db # 。。。 ``` 最终代码结构如下 ![BLOG_20181108_214853_38](/media/blog/images/2018/11/BLOG_20181108_214853_38.png "博客图集BLOG_20181108_214853_38.png") ### 添加全局404页面 在**app/templates/**下创建404.html ```html <!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <title>消失在宇宙星空中的404页面</title> <link href="{{ url_for('static', filename='404/404.css') }}" rel="stylesheet" type="text/css"> </head> <body> <!-- 代码 开始 --> <div class="fullScreen" id="fullScreen"> <img class="rotating" src="{{ url_for('static', filename='404/spaceman.svg') }}"> <div class="pagenotfound-text"> <h1>迷失在太空中!</h1> <h2><a href="#">返回首页</a></h2> </div> <canvas id="canvas2d"></canvas> </div> <script type="text/javascript" src="{{ url_for('static', filename='404/404.js') }}"></script> <!-- 代码 结束 --> </body> </html> ``` 在**app/\_\_init\_\_.py**中添加404视图 ```python # 添加全局404页面 @app.errorhandler(404) def page_not_found(error): return render_template('404.html'), 404 ``` ### 创建管理员登录表单forms.py 修改**app/admin/forms.py** 后台登陆需要验证账号,密码 #### 安装flask表单库 ``` > pip install flask-wtf ``` 安装的版本为 WTForms-2.2.1 flask-wtf-0.14.2 #### 创建登录表单类 **app/admin/forms.py** ```python from flask_wtf import FlaskForm # 表单基类 from wtforms import StringField, PasswordField, SubmitField from wtforms.validators import DataRequired class LoginFrom(FlaskForm): """管理员登录表单""" account = StringField( label='账号', validators=[ DataRequired('请输入账号!') ], description='账号', render_kw={ 'class': "form-control", 'placeholder': "请输入账号", 'required': "required" } ) pwd = PasswordField( label='密码', validators=[ DataRequired('请输入密码!') ], description='密码', render_kw={ 'class': "form-control", 'placeholder': "请输入密码", 'required': "required" } ) submit = SubmitField( label='登录', render_kw={ 'class': "btn btn-primary btn-block btn-flat" } ) ``` ### 后台login()引入表单 ```python from app.admin.forms import LoginFrom @admin.route("/login/") def login(): form = LoginFrom() return render_template('admin/login.html', form=form) ``` ### 修改后台login.html页面的表单 上方为以前的进行注释,下方为表单 ```html {#<input name="user" type="text" class="form-control" placeholder="请输入账号!">#} {{ form.account }} {#<input name="pwd" type="password" class="form-control" placeholder="请输入密码!">#} {{ form.pwd }} {#<a id="btn-sub" type="submit" class="btn btn-primary btn-block btn-flat">登录</a>#} {{ form.submit }} ``` 访问 http://127.0.0.1:5000/admin/login/ 提示缺少**CSRF** ![BLOG_20181108_214907_36](/media/blog/images/2018/11/BLOG_20181108_214907_36.png "博客图集BLOG_20181108_214907_36.png") 需要进行跨站伪装登录验证,通过查阅资料说明后,需要在html的form中添加`{{ form.csrf_token }}`字段 https://flask-wtf.readthedocs.io/en/latest/csrf.html CSRF保护需要一个密钥来安全地对令牌进行签名。默认情况下,这将使用Flask应用程序的`SECRET_KEY`。如果想使用单独的令牌,可以设置`WTF_CSRF_SECRET_KEY`。 这儿直接修改`app/__init__.py`给app添加`SECRET_KEY`。 #### 添加SECRET_KEY 先在终端模拟一个随机的字段,或者自己随便定义就行 ```python import uuid uuid.uuid4().hex 'b1b7ed6af47d4031acbdeb420658ba84' ``` 修改`app/__init__.py` ```python # 。。。补充配置 app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True app.config['SECRET_KEY'] = 'b1b7ed6af47d4031acbdeb420658ba84' # 定义db对象,实例化SQLAlchemy,传入app对象 db = SQLAlchemy(app) # 。。。 ``` #### login中添加csrf_token 在表单中添加`{{ form.csrf_token }}` ```html <form action="" method="post" id="form-data"> <div class="form-group has-feedback"> {#<input name="user" type="text" class="form-control" placeholder="请输入账号!">#} {{ form.account }} <span class="glyphicon glyphicon-envelope form-control-feedback"></span> <div class="col-md-12" id="input_user"></div> </div> <div class="form-group has-feedback"> {#<input name="pwd" type="password" class="form-control" placeholder="请输入密码!">#} {{ form.pwd }} <span class="glyphicon glyphicon-lock form-control-feedback"></span> <div class="col-md-12" id="input_pwd"></div> </div> {{ form.csrf_token }} <div class="row"> <div class="col-xs-8"> </div> <div class="col-xs-4"> {#<a id="btn-sub" type="submit" class="btn btn-primary btn-block btn-flat">登录</a>#} {{ form.submit }} </div> </div> </form> ``` ![BLOG_20181108_214952_79](/media/blog/images/2018/11/BLOG_20181108_214952_79.png "博客图集BLOG_20181108_214952_79.png") ### 视图中处理登录提交的表单 有需要处理post提交的数据,需要指定处理的模式,包含`get`和`post` ```python @admin.route("/login/", methods=['GET', 'POST']) def login(): form = LoginFrom() if form.validate_on_submit(): # 提交的时候验证表单 data = form.data # 获取表单的数据 print(data) return render_template('admin/login.html', form=form) ``` 可以得到值:`{'account': 'user', 'pwd': 'password', 'submit': True, 'csrf_token': 'ImFkMzA0OTZiMWYxZGVkMjVhNmEyZmIzMDAwNGIwMjg2MjljZGY4ZGYi.DqnJxg.MpNuyswOQp-HdRgeJ26Q3X7aVAg'}` ### 在forms.py中进行提交数据验证 验证用户输入的账号,然后通过查询`Admin`数据库,如果查到的集合数量为0,则表明账号不存在,则向前端抛出`ValidationError`。 前端通过遍历`account.errors`来获取里面的错误信息,同样,密码错误信息也做同样的操作。 ```html {% for err in form.account.errors %} <div class="col-md-12" id="input_user" style="color: red">{{ err }}</div> {% endfor %} {% for err in form.pwd.errors %} <div class="col-md-12" id="input_pwd" style="color: red">{{ err }}</div> {% endfor %} ``` ```python from wtforms.validators import DataRequired, ValidationError from app.models import Admin class LoginFrom(FlaskForm): """管理员登录表单""" account = StringField( label='账号', validators=[ DataRequired('请输入账号!') ], description='账号', render_kw={ 'class': "form-control", 'placeholder': "请输入账号", 'required': "required" } ) pwd = PasswordField( label='密码', validators=[ DataRequired('请输入密码!') ], description='密码', render_kw={ 'class': "form-control", 'placeholder': "请输入密码", 'required': "required" } ) submit = SubmitField( label='登录', render_kw={ 'class': "btn btn-primary btn-block btn-flat" } ) def validate_account(self, field): """从Admin数据库中,检测账号是否存在,如果不存在则在account.errors中添加错误信息""" account = field.data admin_num = Admin.query.filter_by(name=account).count() if admin_num == 0: raise ValidationError('账号不存在') ``` 当输入的用户不存在时,会提示`账号不存在` ![BLOG_20181108_215010_91](/media/blog/images/2018/11/BLOG_20181108_215010_91.png "博客图集BLOG_20181108_215010_91.png") ### 在Admin的模型中验证密码是否正确 修改**app/views.py**中的Admin模型,添加密码验证模块 ```python # 定义管理员模型 class Admin(db.Model): # 。。。 def check_pwd(self, input_pwd): """验证密码是否正确,直接将hash密码和输入的密码进行比较,如果相同则,返回True""" from werkzeug.security import check_password_hash return check_password_hash(self.pwd, input_pwd) ``` ### 修改视图中逻辑密码错误提示 当用户提交表单后,从Admin数据库中查询到该登录管理员,然后检查从前端获取的密码是否正确 - 如果密码正确,就需要把账号保存在session中,然后跳转到url参数的next,或者是跳转到后台的首页 - 如果密码错误,就进行错误信息的返回,使用flash消息闪现,前端使用`get_flashed_messages()`来获取错误信息 ```python @admin.route("/login/", methods=['GET', 'POST']) def login(): form = LoginFrom() if form.validate_on_submit(): # 提交的时候验证表单 data = form.data # 获取表单的数据 # print(data) login_admin = Admin.query.filter_by(name=data['account']).first() if not login_admin.check_pwd(data['pwd']): # 判断密码错误,然后将错误信息返回,使用flash用于消息闪现 flash('密码错误!') return redirect(url_for('admin.login')) # 如果密码正确,session中添加账号记录,然后跳转到request中的next,或者是跳转到后台的首页 session['login_admin'] = data['account'] return redirect(request.args.get('next') or url_for('admin.index')) return render_template('admin/login.html', form=form) ``` ### 模板login.html中获取flash的内容 修改login.html,使用`get_flashed_messages()`来获取flash错误信息 ```html {% for msg in get_flashed_messages() %} <p class="login-box-msg" style="color: red">{{ msg }}</p> {% endfor %} ``` 当用户输入一个数据库中存在的账号,但密码错误时,会弹出`密码错误`的提示 ![BLOG_20181108_215051_33](/media/blog/images/2018/11/BLOG_20181108_215051_33.png "博客图集BLOG_20181108_215051_33.png") ### 完成后login.html表单代码 ```html <div class="login-box-body"> {% for msg in get_flashed_messages() %} <p class="login-box-msg" style="color: red">{{ msg }}</p> {% endfor %} <form action="" method="post" id="form-data"> <div class="form-group has-feedback"> {#<input name="user" type="text" class="form-control" placeholder="请输入账号!">#} {{ form.account }} <span class="glyphicon glyphicon-envelope form-control-feedback"></span> {% for err in form.account.errors %} <div class="col-md-12" id="input_user" style="color: red">{{ err }}</div> {% endfor %} </div> <div class="form-group has-feedback"> {#<input name="pwd" type="password" class="form-control" placeholder="请输入密码!">#} {{ form.pwd }} <span class="glyphicon glyphicon-lock form-control-feedback"></span> {% for err in form.pwd.errors %} <div class="col-md-12" id="input_pwd" style="color: red">{{ err }}</div> {% endfor %} </div> {{ form.csrf_token }} <div class="row"> <div class="col-xs-8"> </div> <div class="col-xs-4"> {#<a id="btn-sub" type="submit" class="btn btn-primary btn-block btn-flat">登录</a>#} {{ form.submit }} </div> </div> </form> </div> ``` ## 管理员退出 修改**app/views.py**中的logout函数,当用户点击退出后,则删除该登录账号 ```python @admin.route("/logout/") def logout(): session.pop('login_admin', None) # 删除session中的登录账号 return redirect(url_for("admin.login")) ``` ## 使用装饰器进行访问控制 对于后台的很多视图,是不允许不登陆就能访问的,这时候需要使用装饰器来限制对这些视图的访问权限 使用`url_for('admin.login', next=request.url)`可以指定`next`下一跳的地址 在**app/views.py**中添加 ```python from functools import wraps def admin_login_require(func): @wraps(func) def decorated_function(*args, **kwargs): if session.get('login_admin', None) is None: # 如果session中未找到该键,则用户需要登录 return redirect(url_for('admin.login', next=request.url)) return func(*args, **kwargs) return decorated_function ``` 然后将后台视图中除了`login()`以外的所有所图都加上登录要求`@admin_login_require`,类似如下 ```python @admin.route("/") @admin_login_require def index(): return render_template('admin/index.html') ``` 例如当在用户直接访问 http://127.0.0.1:5000/admin/ 的时候,如果没有登录,则会直接跳转到 http://127.0.0.1:5000/admin/login/?next=http%3A%2F%2F127.0.0.1%3A5000%2Fadmin%2F 这个url的登录页面,输入正确的帐密后,成功返回 http://127.0.0.1:5000/admin/ 页面

很赞哦! (1)

文章交流

  • emoji
0人参与,0条评论

当前用户

未登录,点击   登录

站点信息

  • 建站时间:网站已运行839天
  • 系统信息:Linux
  • 后台程序:Python: 3.6.6
  • 网站框架:Django: 2.2.12
  • 文章统计:250 篇
  • 文章评论:45 条
  • 腾讯分析网站概况-腾讯分析
  • 百度统计网站概况-百度统计
  • 公众号:微信扫描二维码,关注我们
  • QQ群:QQ加群,下载网站的学习源码
返回
顶部
标题 换行 登录
网站