session 和 flask_session 实现原理(源码解析)

一 .session  和 flask_session 实现原理(源码解析)

https://www.cnblogs.com/cwp-bg/p/10084523.html

1.session使用  和源码解析

复制代码
复制代码
除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。本质上是在 Cookies 的基础上实现的,
并且对 Cookies 进行密钥签名要使用会话,所以你需要设置一个密钥secret_key 使用方法: a.导入session:from flask import session b. 设置秘钥:app.secret_key = 'dsfds' (任意字符串) c. 设置session:session['username'] = 'bob' d. 获取session:session['username'] e. 删除:session.pop('username', None) from flask import Flask,session app = Flask(__name__) app.debug=True app.secret_key = 'sadsfasdf' @app.route('/') def index(): session['name'] = 'nick' # 设置session return '首页' @app.route('/test') def test(): print(session['name']) return '获取session' if __name__ == '__main__': app.run() # 在django中发生三件事,1,生成一个随机的字符串 2 往数据库存 3 写入cookie返回浏览器 # 在flask中他没有数据库,但session是怎样实现的? # 生成一个密钥写入这个cookie,然后下次请求的时候,通过这个cookie解密,然后赋值给session # 我们通过app.session_interface来查看
复制代码

 

session源码解析

依据上述session的原理,来分析一下flask框架的session机制实现的过程。

Flask对象使用open_session方法和save_session方法打开和保存会话信息,请求在创建请求上下文后会调用open_session方法获取用户的信息,

在执行完处理逻辑后会调用save_session方法保存用户的信息

复制代码
open_session和save_session
def open_session(self, request): # 调用了app的session_interface对象的方法 return self.session_interface.open_session(self, request) def save_session(self, session, response): return self.session_interface.save_session(self, session, response)
复制代码

 

 app对象默认的session_interface = SecureCookieSessionInterface(),SecureCookieSessionInterface重写了SessionInterface对象的open_session方法和save_session方法。

复制代码
class SecureCookieSessionInterface(SessionInterface):
    pass

    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()

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

        # 检测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)
        # 返回失效的时间点
        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)
复制代码

 

 请求上下文RequestContext的session属性是一个SecureCookieSession对象,可以将其看做一个字典;

总结 :flask默认通过SecureCookieSessionInterface对象管理session,

        其重写了SessionInterface对象的open_session方法和save_session方法,将用户的数据加密后存储在cookie中。

session源码的执行流程
-save_seesion
    -响应的时候,把session中的值加密序列化放大到了cookie中,返回到浏览器中
-open_session
    -请求来了,从cookie中取出值,反解,生成session对象,以后再视图函数中直接用sessoin就可以了
复制代码

 

 2. flask_session使用 和 源码解析

作用:将默认保存的签名cookie中的值,保存到 redis/memcached/file/Mongodb/SQLAlchemy

安装:pip install flask-session

复制代码
复制代码
from flask import Flask,session
from flask_session import RedisSessionInterface
import redis
app = Flask(__name__)
conn=redis.Redis(host='127.0.0.1',port=6379)  # 连接redis

# use_signer是否对key签名
# 如果use_siginer为False,这表示不需要配置app.secret_key
app.session_interface=RedisSessionInterface(conn,key_prefix='lqz')
@app.route('/')
def hello_world():
    session['name']='lqz'
    return 'Hello World!'

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



print("************************************************")


 # 创建一个session对象
 from flask_session import Session
 # 创建一个Session的实例
session = Session()

# 在app初始化时初始化session对象,即加载配置
# __init__.py
from flask import Flask
app = Flask(__name__)
session.init_app(app=app)

# task.py
from Flask import session

@app.route('/test', methods=['POST'])
def test():
    session.get('user',None)
    return ""
复制代码
复制代码
import redis
from flask import Flask,request,session
from flask.sessions import SecureCookieSessionInterface
from flask_session import Session

app = Flask(__name__)

# app.session_interface = SecureCookieSessionInterface()
# app.session_interface = RedisSessionInterface()
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = redis.Redis(host='140.143.227.206',port=6379,password='1234')
Session(app)

@app.route('/login')
def login():
    session['user'] = 'alex'
    return 'asdfasfd'

@app.route('/home')
def index():
    print(session.get('user'))

    return '...'


if __name__ == '__main__':
    app.run()
    # app.__call__
    # app.wsgi_
复制代码

 

源码解析

我们知道session是从app.session_interface入口。所以要改变存储位置的话,要重写app.session_interface。在这里的RedisSessionInterface重写了open_session(取)和save_session(存)。我们来看看RedisSessionInterface这个类

复制代码
class RedisSessionInterface(SessionInterface):
    serializer = pickle
    session_class = RedisSession
    # 实例化参数:redis连接的redis; key_prefix存储的前缀; user_signer是否对key签名,为False则不需要设置secret_key,为True时,需要设置; permanent关闭浏览器是否保存,为Frue保存,为False不保存
    def __init__(self, redis, key_prefix, use_signer=False, permanent=True):
        if redis is None:
            from redis import Redis
            redis = Redis()
        self.redis = redis
        self.key_prefix = key_prefix
        self.use_signer = use_signer
        self.permanent = permanent
        
    def open_session(self, app, request):
        sid = request.cookies.get(app.session_cookie_name)
        if not sid:
            sid = self._generate_sid()
            return self.session_class(sid=sid, permanent=self.permanent)
        if self.use_signer:
            signer = self._get_signer(app)
            if signer is None:
                return None
            try:
                sid_as_bytes = signer.unsign(sid)
                sid = sid_as_bytes.decode()
            except BadSignature:
                sid = self._generate_sid()
                return self.session_class(sid=sid, permanent=self.permanent)

        if not PY2 and not isinstance(sid, text_type):
            sid = sid.decode('utf-8', 'strict')
        val = self.redis.get(self.key_prefix + sid)  # 从redis中取出session
        if val is not None:
            try:
                data = self.serializer.loads(val)  # 获取session反序列化值
                return self.session_class(data, sid=sid)
            except:
                return self.session_class(sid=sid, permanent=self.permanent)
        return self.session_class(sid=sid, permanent=self.permanent)

    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)
        if not session:
            if session.modified:
                self.redis.delete(self.key_prefix + session.sid)
                response.delete_cookie(app.session_cookie_name,
                                       domain=domain, path=path)
            return
        httponly = self.get_cookie_httponly(app)
        secure = self.get_cookie_secure(app)
        expires = self.get_expiration_time(app, session)
        val = self.serializer.dumps(dict(session))  # 将session序列化放入val中
        # 将session设置到redis,name=前缀+字符串,value=session值
        self.redis.setex(name=self.key_prefix + session.sid, value=val,
                         time=total_seconds(app.permanent_session_lifetime))
        if self.use_signer:
            session_id = self._get_signer(app).sign(want_bytes(session.sid))
        else:
            session_id = session.sid
        response.set_cookie(app.session_cookie_name, session_id,
                            expires=expires, httponly=httponly,
                            domain=domain, path=path, secure=secure)
复制代码

 主要是通过配置SESSION_TYPE源码中进行相应的存储:Session(app)源码

def __init__(self, app=None):
    self.app = app
    if app is not None:
        self.init_app(app)

self.init_app(app)源码:

def init_app(self, app):
    app.session_interface = self._get_interface(app)  # 在这里重写了session_interface

self._get_interface(app)源码:

复制代码
def _get_interface(self, app):
    # 在这里做了一些相关的配置
    config = app.config.copy()
    config.setdefault('SESSION_TYPE', 'null')
    config.setdefault('SESSION_PERMANENT', True)
    config.setdefault('SESSION_USE_SIGNER', False)
    config.setdefault('SESSION_KEY_PREFIX', 'session:')
    config.setdefault('SESSION_REDIS', None)
    config.setdefault('SESSION_MEMCACHED', None)
    config.setdefault('SESSION_FILE_DIR',
                      os.path.join(os.getcwd(), 'flask_session'))
    config.setdefault('SESSION_FILE_THRESHOLD', 500)
    config.setdefault('SESSION_FILE_MODE', 384)
    config.setdefault('SESSION_MONGODB', None)
    config.setdefault('SESSION_MONGODB_DB', 'flask_session')
    config.setdefault('SESSION_MONGODB_COLLECT', 'sessions')
    config.setdefault('SESSION_SQLALCHEMY', None)
    config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions')
    
    # 通过if...elif...else来判断匹配config['SESSION_TYPE'],再执行相应的session_interface
    if config['SESSION_TYPE'] == 'redis':
        session_interface = RedisSessionInterface(
            config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'],
            config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
    elif config['SESSION_TYPE'] == 'memcached':
        session_interface = MemcachedSessionInterface(
            config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'],
            config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
    elif config['SESSION_TYPE'] == 'filesystem':
        session_interface = FileSystemSessionInterface(
            config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'],
            config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'],
            config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
    elif config['SESSION_TYPE'] == 'mongodb':
        session_interface = MongoDBSessionInterface(
            config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'],
            config['SESSION_MONGODB_COLLECT'],
            config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
            config['SESSION_PERMANENT'])
    elif config['SESSION_TYPE'] == 'sqlalchemy':
        session_interface = SqlAlchemySessionInterface(
            app, config['SESSION_SQLALCHEMY'],
            config['SESSION_SQLALCHEMY_TABLE'],
            config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
            config['SESSION_PERMANENT'])
    else:
        session_interface = NullSessionInterface()
    return session_interface
复制代码

 

复制代码
问题:设置cookie时,如何设定关闭浏览器则cookie失效。
response.set_cookie('k','v',exipre=None)#这样设置即可
#在session中设置

app.session_interface=RedisSessionInterface(conn,key_prefix='lqz',permanent=False) #一般不用,我们一般都设置超时时间,多长时间后失效 问题:cookie默认超时时间是多少?如何设置超时时间 #源码expires = self.get_expiration_time(app, session) 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),#这个配置文件控制

posted on 2020-03-05 16:49  枫飞飞  阅读(492)  评论(0编辑  收藏  举报