【百度云搜索,搜各种资料:http://www.81ad.cn】
Flask 构建微电影视频网站
已上线演示地址: http://movie.tbquan.cn
管理员登录
-
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
中
app/init.py中修改
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:[email protected]:3306/movie" # 定义数据库连接,传入连接,默认端口3306,可不写
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+mysqlconnector://root:[email protected]: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配置就不需要了
from app import db
# 。。。
最终代码结构如下
添加全局404页面
在app/templates/下创建404.html
消失在宇宙星空中的404页面
迷失在太空中!
返回首页
在app/init.py中添加404视图
# 添加全局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
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()引入表单
from app.admin.forms import LoginFrom
@admin.route("/login/")
def login():
form = LoginFrom()
return render_template('admin/login.html', form=form)
修改后台login.html页面的表单
上方为以前的进行注释,下方为表单
{##}
{{ form.account }}
{##}
{{ form.pwd }}
{#登录#}
{{ form.submit }}
访问 http://127.0.0.1:5000/admin/login/ 提示缺少CSRF
需要进行跨站伪装登录验证,通过查阅资料说明后,需要在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
先在终端模拟一个随机的字段,或者自己随便定义就行
import uuid
uuid.uuid4().hex
'b1b7ed6af47d4031acbdeb420658ba84'
修改app/__init__.py
# 。。。补充配置
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
app.config['SECRET_KEY'] = 'b1b7ed6af47d4031acbdeb420658ba84'
# 定义db对象,实例化SQLAlchemy,传入app对象
db = SQLAlchemy(app)
# 。。。
login中添加csrf_token
在表单中添加{{ form.csrf_token }}
视图中处理登录提交的表单
有需要处理post提交的数据,需要指定处理的模式,包含get
和post
@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
来获取里面的错误信息,同样,密码错误信息也做同样的操作。
{% for err in form.account.errors %}
{{ err }}
{% endfor %}
{% for err in form.pwd.errors %}
{{ err }}
{% endfor %}
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('账号不存在')
当输入的用户不存在时,会提示账号不存在
在Admin的模型中验证密码是否正确
修改app/views.py中的Admin模型,添加密码验证模块
# 定义管理员模型
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()
来获取错误信息
@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错误信息
{% for msg in get_flashed_messages() %}
{{ msg }}
{% endfor %}
当用户输入一个数据库中存在的账号,但密码错误时,会弹出密码错误
的提示
完成后login.html表单代码
{% for msg in get_flashed_messages() %}
{{ msg }}
{% endfor %}
管理员退出
修改app/views.py中的logout函数,当用户点击退出后,则删除该登录账号
@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中添加
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
,类似如下
@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/ 页面