flask基础(中篇)
目录
一、session/cookie介绍
二、使用session实现登录验证
三、flask扩展之flask-login
session/cookie
前言
Session和Cookie的结合使用,一般有两种存储方式:
第一种: session数据存储在客户端: Flask采用'secure cookie'方式保存session,即session数据是使用base64编码后保存在客户端的cookie中。也就是说无须依赖第三方数据库保存session数据。
第二种: session数据存储在服务端,分为以下三步骤:
步骤1: 当客户端发送请求到服务端的时候,服务端会校验请求中cookie参数中的sessionid值,如果cookie中不存在sessionid则认为客户端访问服务端时,是发起了一个新的会话。
步骤2: 如果是新的会话,则服务端会传递给客户端一个cookie,并在cookie中存储一个新的sessionid值,并将相关数据保存在session中。
步骤3: 客户端下次再发送请求的时候,请求上下文对象会携带cookie,通过校验cookie中的sessionid值,即可判断是否是同一会话。
步骤4: 如果校验会话是同一会话,则可以从session中获取到之前保存的数据。
访问者的标识问题服务器需要识别来自同一访问者的请求。这主要是通过浏览器的cookie实现的。 访问者在第一次访问服务器时,服务器在其cookie中设置一个唯一的ID号——会话ID(session)。 这样,访问者后续对服务器的访问头中将自动包含该信息,服务器通过这个ID号,即可区 隔不同的访问者。
1. Cookie
概念:
a)客户端会话技术,浏览器的会话技术
b)数据全部存储在客户端中
c)存储使用的键值对结构进行存储
特性:
支持过期时间
默认会自动携带本网站的cookie
不能跨域名
不能跨浏览器
创建:
Cookie是通过服务器创建的Response来创建的
设置:set_cookie('key', value, max_ages='', expires='')
删除, 有三种删除方式
1. 直接清空浏览器的cookie
2. delete_cookie('key') 直接使用delete_cookie函数
3. set_cookie('key','',expires=0) 重新设置key的值为空,过期时间为0
获取:
在每次请求中,url都会向服务器传递Request,在request中可以获取到cookie的信息
request.cookies.get('name')
例子1,设置cookie:
import datetime
@blue.route('/setcookie/')
def set_cookie():
temp = render_template('index.html')
response = make_response(temp)
outdate=datetime.datetime.today() + datetime.timedelta(days=30)
# 设置cookie中的name的存在时长,设置为30天才过期
response.set_cookie('name','cocoococo',expires=outdate)
return response
例子2,删除cookie中的值
@blue.route('/setcookie/')
def set_cookie():
temp = render_template('index.html')
response = make_response(temp)
# 第一种方式,通过set_cookie去删除
response.set_cookie('name','',expires=0)
# 第二种方式,del_cookie删除
response.del_cookie('name')
return response
例子3,获取cookie中的值
@blue.route('/getcookie/')
def get_cookie():
name=request.cookies.get('name')
return name
2. 将session数据存储在数据库
flask-session是flask框架的session组件
该组件则将支持session保存到多个地方
如:
redis:保存数据的一种工具,五大类型。非关系型数据库
memcached
mongodb
sqlalchmey:那数据存到数据库表里面
2.1 安装
pip install flask-session
如果指定存session的类型为redis的话,需要安装redis
pip install redis
2.2 语法
设置session:
session['key'] = value
读取session:
result = session['key'] :如果内容不存在,将会报异常
result = session.get('key') :如果内容不存在,将返回None
删除session:
session.pop('key')
清空session中所有数据:
session.clear()
2.2 使用
我们在初始化文件中创建一个方法,通过调用该方法来获取到Flask的app对象
def create_app():
app = Flask(__name__)
# SECRET_KEY 秘钥
app.config['SECRET_KEY'] = 'secret_key'
# session类型为redis
app.config['SESSION_TYPE'] = 'redis'
# 添加前缀
app.config['SESSION_KEY_PREFIX'] = 'flask'
# 加载app的第一种方式
se = Session()
se.init_app(app=app)
#加载app的第二种方式
Session(app=app)
app.register_blueprint(blueprint=blue)
return app
2.3 案例
定义一个登陆的方法,post请求获取到username,直接写入到redis中,并且在页面中展示出redis中的username
a)需要先启动redis,开启redis-server,使用redis-cli进入客户端
b)定义方法
@blue.route('/login/', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
username = session.get('username')
return render_template('login.html', username=username)
else:
username = request.form.get('username')
session['username'] = username
return redirect(url_for('first.login'))
c)定义模板
<body>
<h3>欢迎:{{ username }}</h3>
<form action="" method="POST">
用户名:<input type="text" name="username" placeholder="请输入你的名字">
<input type="submit" value="提交">
</form>
</body>
d)redis中数据
注意:我们在定义app.config的时候指定了SESSION_KEY_PREFIX为flask,表示存在session中的key都会加一个前缀名flask
e) cookie和session的联系
访问者在第一次访问服务器时,服务器在其cookie中设置一个唯一的ID号——会话ID(session)。 这样,访问者后续对服务器的访问头中将自动包含该信息,服务器通过这个ID号,即可区 隔不同的访问者。然后根据不同的访问者来获取其中保存的value值信息。
使用session实现登录验证
第一种方式: flask默认的session/cookie使用方式
将session中的数据存储在cookie中,并返回给客户端。
缺点:数据存储在cookie中,不安全
1. 前端login.html页面
登录页面就两个输入框,分别接收用户名和密码
<dd class="user_icon">
<input type="text" name="username" placeholder="账号" class="login_txtbx"/>
</dd>
<dd class="pwd_icon">
<input type="password" name="password" placeholder="密码" class="login_txtbx"/>
</dd>
2. 后端方法
@blue.route('login/', methods=['GET'])
def login():
if request.method == 'GET':
# 获取提交的用户名和密码
username = request.args.get('username')
password = request.args.get('password')
# 模拟判断用户名和密码
if username == '小明' and password == '123456':
# 启动permanent修改为True
session.permanent = True
# 在session中记录登录状态
session['login_status'] = 1
return '登录成功'
else:
return '登录失败'
3. 修改启动manage.py文件中定义加密以及过期时间
# session加密方式
app.secret_key = '123'
# 设置过期时间,5秒后session失效
app.permanent_session_lifetime = 5
注意:
1)设置一个持久化会话的存活时间,必须修改session.permanent的属性和flask对象app的permanent_session_lifetime属性,permanent_session_lifetime属性作为datetime.timedelta对象,从Flask0.8开始也可以用一个整数表示多少秒后过期。
2)加密的强度取决于SECRET_KEY的复杂程度。一般SECRET_KEY可以通过os.urandom(24)随机生成。
第二种方式: 使用flask_session扩展库实现登录功能描述
使用session实现用户的模拟登陆功能。在前面已经说过了,在用户第一次访问服务端的时候,在服务端的redis中会创建一个session值,在客户端浏览器的cookies中也会创建一个session的值。该cookies中的session值和redis中的session值是一样的,那么在往后的访问操作中,请求request都会传递给后端,后端在获取到request的时候,其实就是获取到了request.cookies中的session的值了,那么就可以做登录的校验了。校验功能如下:
1. 前端login.html页面
登录页面就两个输入框,分别接收用户名和密码
<dd class="user_icon">
<input type="text" name="username" placeholder="账号" class="login_txtbx"/>
</dd>
<dd class="pwd_icon">
<input type="password" name="password" placeholder="密码" class="login_txtbx"/>
</dd>
2. 后端方法
模拟用户的登录,直接判断用户的名称为妲己以及密码为123123.如果验证成功,就向session中保存用户的id值。如果没有登录成功的话,那就对session不做任何的处理,直接跳转到登录页面上去。
@app_blue.route('/new_login/', methods=['GET', 'POST'])
def new_login():
if request.method == 'GET':
return render_template('login.html')
else:
username = request.form.get('username')
password = request.form.get('password')
# 数据库校验,用户密码是否正确
if username == '妲己' and password == '123123':
session['user_id'] = 1
return redirect((url_for('first.index')))
else:
return redirect(url_for('first.new_login'))
@app_blue.route('/index/', methods=['GET'])
def index():
return render_template('index.html')
3. 修改启动manage.py文件中定义加密以及过期时间
# 配置session
from flask_session import Session
# 指定redis作为缓存数据库
app.config['SESSION_TYPE'] = 'redis'
# 指定访问哪一个redis,ip和端口
app.config['SESSION_REDIS'] = redis.Redis(host='127.0.0.1', port=6379)
# 初始化app
se = Session()
se.init_app(app=app)
装饰器
使用装饰器去装饰我们的index()函数,如果用户登录了,则session中有user_id的key,如果没有登录的话,session中是没有user_id的key的。那么验证用户是否登录了,其实就是验证session的user_id
def is_login(func):
@wraps(func)
def check_login(*args, **kwargs):
if 'user_id' in session:
return func(*args, **kwargs)
else:
return redirect(url_for('first.new_login'))
return check_login
修改index()函数,使用装饰器装饰
@app_blue.route('/index/', methods=['GET'])
@is_login
def index():
return render_template('index.html')
flask扩展之flask-login
前言
在flask中如何快速的实现登录注册注销功能,以及登录状态验证等功能? flask的扩展库中有Flask-Login库就可快速的实现以上的功能,实现起来是非常的便利。
1. 安装Flask-Login
pip install flask-login
2. 实现登录功能
2.1 定义login.html模板
{% extends 'base.html' %}
{% block title %}
登录页面
{% endblock %}
{% block content %}
<form action="" method="post">
姓名:<input type="text" name="username">
密码:<input type="text" name="password">
<input type="submit" value="提交">
</form>
{% endblock %}
2.2 实现登录功能
登录方法中定义被login_manager.user_loader装饰的回调函数,回调函数在如下两个地方被调用:
1)该函数表明当前用户登录成功时调用login_user()方法时,会被回调的函数。回调函数实现的功能是向会话上下文session中存储最为中间的键值对,key为user_id, value为当前登录用户的ID值。
2)回调函数在访问任何一个路由地址时也会被调用。
注意: 因为请求上下文在每次建立连接时,都需要获取当前登录用户并将当前登录用户设置为全局上下文current_user,因此回调函数返回的是当前登录系统的用户对象。
from flask_login import LoginManager, login_required, login_user, logout_user,current_user
# 获取登录管理对象
login_manager = LoginManager()
@login_manager.user_loader
def load_user(user_id):
# 必须编写一个函数用于从数据库加载用户。
# 这个函数在login_user(user)存储当前登录用户到session中时,会被调用
# 在每次访问地址的时候都被被调用,用于向请求上下文中绑定当前登录的用户信息
return User.query.get(user_id)
@blue.route('/login/', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
# 校验用户名和密码是否填写完成
if not all([username, password]):
return render_template('login.html')
# 通过用户名获取用户对象
user = User.query.filter_by(username=username).first()
# 校验密码是否正确
if check_password_hash(user.password, password):
# 实现登录
# login_user()能够将已登录并通过load_user()的用户对应的User对象保存在session中
# 在session中会创建一个键值对,key为user_id,value为当前登录用户的id值
# 如果希望应用记住用户的登录状态, 只需要为 login_user()的形参 remember 传入 True 实参就可以了.
login_user(user)
return redirect(url_for('user.index'))
else:
flash('用户名或者密码错误')
return redirect(url_for('user.index'))
2.3 启动文件进行配置
session_protection: 设置存储用户登录状态的安全级别
login_view: 设置登录验证失败的跳转地址
from user.views import login_manager
app.config['SECRET_KEY'] = os.urandom(24)
# 登录管理,初始化app
# 可以设置None,'basic','strong'以提供不同的安全等级,一般设置strong,如果发现异常会登出用户
# session_protection 能够更好的防止恶意用户篡改 cookies, 当发现 cookies 被篡改时, 该用户的 session 对象会被立即删除, 导致强制重新登录。
login_manager.session_protection='strong'
# 当登录认证不通过,则跳转到该地址
login_manager.login_view='user.login'
login_manager.init_app(app)
2.4 访问首页,登录校验
使用装饰器login_required()进行登录校验。
核心思想: 校验session中是否存在key为user_id的键值对。如果校验成功,则继续访问被装饰的函数。如果校验失败,则跳转到启动文件中定义的login_manager.login_view定义的视图函数。
@blue.route('/index/')
@login_required
def index():
return render_template('index.html')
如果登录校验成功,则渲染index.html首页,在页面中可以解析全局变量current_user参数。
{% extends 'base.html' %}
{% block title %}
首页页面
{% endblock %}
{% block content %}
<p>我是首页</p>
<p>当前登录系统用户为: {{ current_user.username }}</p>
{% endblock %}
2.5 注销
使用logout_user()方法实现注销,核心功能就是删除当前会话上下文session中的user_id键值对。
# 退出
@blue.route('/logout/', methods=['GET'])
@login_required
def logout():
logout_user()
return redirect(url_for('user.login'))