Flask 的 Session


1. Flask 的 session 原理

  • 当请求刚进来时,Flask 会读取cookie中session对应的值:eyjrmindxxx,将该值解密并反序列化成一个字典,并且放入内存中以便视图函数使用
  • 当请求结束时,flask会读取内存中字典的值,进行序列化+加密,最后写入用户cookie中


2. Flask 的 session 介绍

  • Flask 中的 session 本质上继承了 python 中的字典,字典有的方法它都能使用,且在返回给浏览器的时候会将这个字典变成密文写入cookie中

3. session 的全局配置项

{
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),  # session 的有效时间
    'SESSION_COOKIE_NAME': 'session',  # session 在 cookie 中的名字
    'SESSION_COOKIE_SAMESITE': None,
    'SESSION_COOKIE_DOMAIN': None,
    'SESSION_COOKIE_PATH': None,
    'SESSION_COOKIE_HTTPONLY': True,
    'SESSION_COOKIE_SECURE': False,
    'SESSION_REFRESH_EACH_REQUEST': True,  # 是否当用户访问了该页面重新计算session时间
}

4. session 的使用

# app.py

from flask import Flask, request, session

app = Flask(__name__)
app.secret_key = 'session_salt'  # 设置session的“盐”,一定要设置否则无法使用 session


@app.route('/index', methods=['GET', 'POST'])
def index():
session['name'] = 'Kevin'  # 设置 session
    session_name_data = session.get('name') # 获取 session
    print(session_name_data)  # Kevin
del session['name']  # 删除 session
    return 'index'


if __name__ == '__main__':
    app.run()

Flask 的 Session 源码分析


  • flask 默认通过 SecureCookieSessionInterface 对象管理 Session

  • SecureCookieSessionInterface 对象下最重要的两个方法就是 open_session方法 和 save_session方法

  • open_session方法 和 save_session方法的说明

    • open_session方法: 获取session,从cookie中获取session的信息,然后session信息进行解密+反序列化得到相关的session内容

    • save_session方法: 保存session,将需要保存的内容加密+序列化,然后写入cookie中

class SecureCookieSessionInterface(SessionInterface):

……

# 获取 session 的方法
    def open_session(self, app, request):
# 检测是否设置了secret_key参数,返回一个签名对象
        s = self.get_signing_serializer(app)
        if s is None:
            return None
# 去cookie中获取session信息
        val = request.cookies.get(app.session_cookie_name)
# 如果是第一次请求,返回一个空的SecureCookieSession对象,会被交给请求上下文的session属性管理
        if not val:
            return self.session_class()
# 获取session的失效时间
        max_age = total_seconds(app.permanent_session_lifetime)
        try:
# 对session信息进行解码+反序列化得到用户信息
data = s.loads(val, max_age=max_age)
# 返回有用户信息的session对象
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

# 保存 session 的方法
    def save_session(self, app, session, response):
# 获取cookie设置的域
        domain = self.get_cookie_domain(app)
# 获取cookie设置的路径
        path = self.get_cookie_path(app)

        if not session:
            if session.modified:
                response.delete_cookie(
                    app.session_cookie_name, domain=domain, path=path
                )
            return

        if session.accessed:
            response.vary.add("Cookie")

# 检测SESSION_REFRESH_EACH_REQUEST参数配置
        if not self.should_set_cookie(app, session):
            return
# 返回SESSION_COOKIE_HTTPONLY参数配置
        httponly = self.get_cookie_httponly(app)
# 返回SESSION_COOKIE_SECURE参数配置
        secure = self.get_cookie_secure(app)
# 返回SESSION_COOKIE_SAMESITE参数配置
        samesite = self.get_cookie_samesite(app)
# 返回失效的时间点
        expires = self.get_expiration_time(app, session)
# 将用户的数据加密+序列化
 val = self.get_signing_serializer(app).dumps(dict(session))
# 设置cookie
        response.set_cookie(
            app.session_cookie_name,
            val,
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            samesite=samesite,
        )

  • app.session_interface 说明

    • app.session_interface 是指定 Flask 使用什么对象对session进行管理

    • 当 app.session_interface 被修改了,那么就会使用你所指定的对象进行对session的管理

    • flask-session 模块也是通过修改 app.session_interface 的值来确定session保存在那个位置

from flask import Flask
from flask.sessions import SecureCookieSessionInterface

app = Flask(__name__)
app.session_interface = SecureCookieSessionInterface()

flask-session模块


1. PIL的安装

pip3 install flask-session -i https://pypi.douban.com/simple # 使用豆瓣的镜像

2. 介绍

  • flask-session 是 flask 框架的 session组件,由于原来 flask 内置 session 使用签名 cookie 保存,该组件则将支持 session 保存到多个地方,如:

    • null: 采用flask默认的保存在cookie中
    • redis: 将数据保存到 redis 缓存中
    • memcached: 将数据保存到 memcached 缓存中
    • filesystem: 将数据保存到文件中
    • mongodb: 将数据保存到 mongodb 数据库中
    • sqlalchmey:将数据存到关系型数据库

  • flask-session 实际是通过修改 app.session_interface 的值来确定 session 保存在那个位置

    • 在 Flask 的 Session 源码分析栏目中有提到 app.session_interface 的说明


3. 使用场景

  • 如果应用程序比较小,就用原生的加密ccokie 保存session(即: Flask 内置session)

  • 如果应用程序比较大,就用flask-session(即: redis)

4. 所有配置项

# 可以在 flask-session 所提供的 Session 类源码中查看所有的配置信息

# from flask.ext.session import Session  # 旧版本的写法
from flask_session import Session  # 新版本的写法

Session

# ------------------ 公共配置 --------------------------

app.config['SESSION_TYPE'] = 'null'  # session所保存的位置
app.config['SESSION_PERMANENT'] = True  # 是否使用永久会话,默认True,但是如果设置了PERMANENT_SESSION_LIFETIME,则这个失效
app.config['SESSION_USE_SIGNER'] = False  # 是否为cookie设置签名来保护数据不被更改,默认是False;如果设置True,那么必须设置flask的secret_key参数
app.config['SESSION_KEY_PREFIX'] = 'session:'  # session存储时的key的前缀

# ------------------ redis的相关配置 --------------------------

app.config['SESSION_REDIS'] = None  # 配置redis连接对象(即: 设置该连接那个redis),如果不设置默认使用本地ip+6379端口的redis进行连接

# ------------------ memcached的相关配置 --------------------------

app.config['SESSION_MEMCACHED'] = None  # 配置memcached连接对象(即: 设置该连接那个memcached),如果不设置默认就会报错

# ------------------ filesystem的相关配置 --------------------------

app.config['SESSION_FILE_DIR'] = os.path.join(os.getcwd(), 'flask_session')  # 文件路径
app.config['SESSION_FILE_THRESHOLD'] = 500  # 存储session的个数如果大于这个值时,就要开始进行删除了
app.config['SESSION_FILE_MODE'] = 384  # 文件权限类型,默认值: 0o600

# ------------------ mongodb的相关配置 --------------------------

app.config['SESSION_MONGODB'] = None  # 所要连接的MongoDB数据库
app.config['SESSION_MONGODB_DB'] = 'flask_session'  # MongoDB 数据库名称
app.config['SESSION_MONGODB_COLLECT'] = 'sessions'  # MongoDB 表名称

# ------------------ sqlalchmey的相关配置 --------------------------

app.config['SESSION_SQLALCHEMY'] = None  # SQLAlchemy对象
app.config['SESSION_SQLALCHEMY_TABLE'] = 'sessions'  # 表名称(即: session 数据保存在该表下)

5. 使用

  • redis

# app.py

import redis
from flask import Flask, render_template, session
# from flask.ext.session import Session  # 旧版本的写法
from flask_session import Session  # 新版本的写法

app = Flask(__name__)
app.secret_key = 'session_salt'  # 设置session的“盐”

# ----------------- flask-session的相关配置-start -------------------------

app.config['SESSION_TYPE'] = 'redis'  # session所保存的位置
app.config['SESSION_REDIS'] = redis.Redis(host='127.0.0.1', port=6379, password='')  # 配置redis连接对象(即: 设置该连接那个redis),如果不设置默认使用本地ip+6379端口的redis进行连接

app.config['SESSION_PERMANENT'] = True  # 是否使用永久会话,默认True,但是如果设置了PERMANENT_SESSION_LIFETIME,则这个失效
app.config['SESSION_USE_SIGNER'] = False  # 是否为cookie设置签名来保护数据不被更改,默认是False;如果设置True,那么必须设置flask的secret_key参数
app.config['SESSION_KEY_PREFIX'] = 'session:'  # session存储时的key的前缀

Session(app)

# ----------------- flask-session的相关配置-end -------------------------


@app.route('/login')
def login():
    session['user'] = 'Kevin'  # 设置 session
    return render_template('login.html')


@app.route('/index')
def index():
    print(session.get('user'))  # 获取 session
    return render_template('index.html')


if __name__ == '__main__':
    app.run()

  • memcached

# app.py

import memcache
from flask import Flask, render_template, session
# from flask.ext.session import Session  # 旧版本的写法
from flask_session import Session  # 新版本的写法

app = Flask(__name__)
app.secret_key = 'session_salt'  # 设置session的“盐”

# ----------------- flask-session的相关配置-start -------------------------

app.config['SESSION_TYPE'] = 'memcached'  # session所保存的位置
app.config['SESSION_MEMCACHED'] memcache.Client(['10.211.55.4:12000']) # 配置memcached连接对象(即: 设置该连接那个memcached),如果不设置默认就会报错

app.config['SESSION_PERMANENT'] = True  # 是否使用永久会话,默认True,但是如果设置了PERMANENT_SESSION_LIFETIME,则这个失效
app.config['SESSION_USE_SIGNER'] = False  # 是否为cookie设置签名来保护数据不被更改,默认是False;如果设置True,那么必须设置flask的secret_key参数
app.config['SESSION_KEY_PREFIX'] = 'session:'  # session存储时的key的前缀

Session(app)

# ----------------- flask-session的相关配置-end -------------------------


@app.route('/login')
def login():
    session['user'] = 'Kevin'  # 设置 session
    return render_template('login.html')


@app.route('/index')
def index():
    print(session.get('user'))  # 获取 session
    return render_template('index.html')


if __name__ == '__main__':
    app.run()

  • filesystem

# app.py

from flask import Flask, render_template, session
# from flask.ext.session import Session  # 旧版本的写法
from flask_session import Session  # 新版本的写法

app = Flask(__name__)
app.secret_key = 'session_salt'  # 设置session的“盐”

# ----------------- flask-session的相关配置-start -------------------------

app.config['SESSION_TYPE'] = 'filesystem'  # session所保存的位置
app.config['SESSION_FILE_DIR'] = '/Users/wupeiqi/PycharmProjects/grocery/96.Flask新课程/组件/2.flask-session'  # 文件路径
app.config['SESSION_FILE_THRESHOLD'] = 500  # 存储session的个数如果大于这个值时,就要开始进行删除了
app.config['SESSION_FILE_MODE'] = 384  # 文件权限类型,默认值: 0o600

app.config['SESSION_PERMANENT'] = True  # 是否使用永久会话,默认True,但是如果设置了PERMANENT_SESSION_LIFETIME,则这个失效
app.config['SESSION_USE_SIGNER'] = False  # 是否为cookie设置签名来保护数据不被更改,默认是False;如果设置True,那么必须设置flask的secret_key参数
app.config['SESSION_KEY_PREFIX'] = 'session:'  # session存储时的key的前缀

Session(app)

# ----------------- flask-session的相关配置-end -------------------------


@app.route('/login')
def login():
    session['user'] = 'Kevin'  # 设置 session
    return render_template('login.html')


@app.route('/index')
def index():
    print(session.get('user'))  # 获取 session
    return render_template('index.html')


if __name__ == '__main__':
    app.run()

  • mongodb

# app.py

import pymongo
from flask import Flask, render_template, session
# from flask.ext.session import Session  # 旧版本的写法
from flask_session import Session  # 新版本的写法

app = Flask(__name__)
app.secret_key = 'session_salt'  # 设置session的“盐”

# ----------------- flask-session的相关配置-start -------------------------

app.config['SESSION_TYPE'] = 'mongodb'  # session所保存的位置
app.config['SESSION_MONGODB'] = pymongo.MongoClient('mongodb://localhost:27017/')  # 所要连接的MongoDB数据库
app.config['SESSION_MONGODB_DB'] = 'mongo的db名称(数据库名称)'  # MongoDB 数据库名称
app.config['SESSION_MONGODB_COLLECT'] = 'mongo的collect名称(表名称)'  # MongoDB 表名称

app.config['SESSION_PERMANENT'] = True  # 是否使用永久会话,默认True,但是如果设置了PERMANENT_SESSION_LIFETIME,则这个失效
app.config['SESSION_USE_SIGNER'] = False  # 是否为cookie设置签名来保护数据不被更改,默认是False;如果设置True,那么必须设置flask的secret_key参数
app.config['SESSION_KEY_PREFIX'] = 'session:'  # session存储时的key的前缀

Session(app)

# ----------------- flask-session的相关配置-end -------------------------


@app.route('/login')
def login():
    session['user'] = 'Kevin'  # 设置 session
    return render_template('login.html')


@app.route('/index')
def index():
    print(session.get('user'))  # 获取 session
    return render_template('index.html')


if __name__ == '__main__':
    app.run()

  • sqlalchmey

pip3 install flask-sqlalchemy -i https://pypi.douban.com/simple # 使用豆瓣的镜像

# app.py

from flask import Flask, render_template, session
# from flask.ext.session import Session  # 旧版本的写法
from flask_session import Session  # 新版本的写法
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.secret_key = 'session_salt'  # 设置session的“盐”

# ----------------- flask-session的相关配置-start -------------------------

# 设置数据库链接
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://数据库用户名:密码,如果没有密码可以空着不填@ip地址:端口号/数据库名称?charset=utf8'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

# 实例化SQLAlchemy
db = SQLAlchemy(app)

app.config['SESSION_TYPE'] = 'sqlalchemy'  # session所保存的位置
app.config['SESSION_SQLALCHEMY'] = db  # SQLAlchemy对象
app.config['SESSION_SQLALCHEMY_TABLE'] = 'session'  # 表名称(即: session 数据保存在该表下)

app.config['SESSION_PERMANENT'] = True  # 是否使用永久会话,默认True,但是如果设置了PERMANENT_SESSION_LIFETIME,则这个失效
app.config['SESSION_USE_SIGNER'] = False  # 是否为cookie设置签名来保护数据不被更改,默认是False;如果设置True,那么必须设置flask的secret_key参数
app.config['SESSION_KEY_PREFIX'] = 'session:'  # session存储时的key的前缀

Session(app)

# ----------------- flask-session的相关配置-end -------------------------


@app.route('/login')
def login():
    session['user'] = 'Kevin'  # 设置 session
    return render_template('login.html')


@app.route('/index')
def index():
    print(session.get('user'))  # 获取 session
    return render_template('index.html')


if __name__ == '__main__':
    app.run()

# 在终端上执行创建数据库表

>>> from app import db
>>> db.create_all()  # 创建数据库表