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号,即可区 隔不同的访问者。

官网文档地址

概念:

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'))
原文地址:https://www.cnblogs.com/zhangliang91/p/11109402.html