博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
flask-session
阅读量:5085 次
发布时间:2019-06-13

本文共 7568 字,大约阅读时间需要 25 分钟。

  Flask中的session处理机制

  • 请求刚到来,获取随机字符串,存在则去“数据库”中获取原来的个人数据,否则创建一个空的容器-->内存:对象(随机字符串 ,存放数据容器)
  • 视图:操作 内存中的对象(随机字符串 ,存放数据容器)
  • 响应:把内存对象数据保存到数据库中,并把随机字符串更新到用户的cookie中

 

  之前我们对flask源码进行分析过,在RequestContext中不仅封装了request的数据,还是封装了session,只不过此时为none

  不过在执行ctx.push()方法中,执行到_request_ctx_stack.push(self),也是LocalStack对象把RequestContext对象放到local中...之后,还有这么一段代码

self.session = self.app.open_session(self.request)        if self.session is None:            self.session = self.app.make_null_session()

   似乎上面这个过程就对ctx里session进行重新赋值,那这个session到底是一个什么对象呢?

  上面self.app就是flask对象,在open_session返回这么一玩意

self.session_interface.open_session(self, request)

   这self.session_interface又是个什么鬼?是SecureCookieSessionInterface()对象,并调用它里面的open_session方法,还是传入了request的,还在这个方法中,最终执行了return self.session_class(data),其实就是一个SecureCookieSession对象,而在SecureCookieSession这个类的一个父类是继承了字典,所以SecureCookieSession对象是一个特殊的字典,RequestContext对象封装的session也就是一个特殊的字典

class CallbackDict(UpdateDictMixin, dict):

   再次回到open_session的源码中

def open_session(self, app, request):        #s就相当于加密规则,里面有涉及到secret_key        s = self.get_signing_serializer(app)        if s is None:            return None        #去用户请求的cookie中获取原来给你的随机字符串        #去cookie中获取key为session的值        val = request.cookies.get(app.session_cookie_name)        if not val:            #如果没有,就创建一个特殊的空字典            return self.session_class()        max_age = total_seconds(app.permanent_session_lifetime)        try:         #把从cookie里获取的session值反序列化回字典            data = s.loads(val, max_age=max_age)            return self.session_class(data)        except BadSignature:            return self.session_class()

   最后我们会发现在ctx.push()里,请求刚进来时,会给封装在RequestContext对象下的session创建一个特殊的空字典

  请求处理中,比如进行session['xxx'] = 123操作,会执行session的__setitem__方法,注意这个session是全局导入的,所以它是LocalProxy对象,流程又是这么熟悉,执行偏函数,获取session对象(这里就是特殊空字典),self._get_current_object()就是这个特殊的空字典,也是SecureCookieSession对象

self._get_current_object()[key] = value

  [key] = value这代码又会执行SecureCookieSession里的__setitem__方法,它本身没有实现该方法,所以它调用父类字典中的方法

 

  响应时,在response = self.full_dispatch_request()中,最后会执行self.finalize_request(rv),并在这里面执行的response = self.process_response(response),返回response前还执行了如下代码

if not self.session_interface.is_null_session(ctx.session):         #ctx.session为那个特殊字典,执行视图后,里面现在是有值的            self.save_session(ctx.session, response)

   save_session中,执行了self.session_interface.save_session,而这个session_interface就是SecureCookieSessionInterface对象,执行它里面save_session方法

def save_session(self, app, session, response):        #这里self是配置文件对象,这些get操作是从配置文件读取session的相关的一些配置        domain = self.get_cookie_domain(app)        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 not self.should_set_cookie(app, session):            return        httponly = self.get_cookie_httponly(app)        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)

   so,flask内置的session,是将session保存在加密的cookie中实现

 

  但是放在cookie会有两个方面的考虑:长度和安全,所以还需要了解第三方session

  安装 pip3 install Flask-Session,这个 组件里支持了session的各种存储方法:Redis,Memcached,文件,数据库(mongodb,sqlalchemy)

  上面源码分析过,在self.session_interface.save_session中,self.session_interface就是SecureCookieSessionInterface(),在它里面直接是把序列化后的session写入到cookie中,没有进行服务端的存储,而我们希望就是把session随机字符串写入cookie中,其他的值存储到我们指定的地方去,所以我们需要把SecureCookieSessionInterface这个类替换成第三方session提供的类

from flask import Flask,sessionapp = Flask(__name__)app.secret_key = 'suijksdfsd'# 指定session_interface 类from redis import Redisfrom flask_session import RedisSessionInterfaceconn = Redis()  #ip 端口连接#permanent为true时,关闭浏览器,cookie就失效app.session_interface = RedisSessionInterface(conn,key_prefix='__',use_signer=False,permanent=True)  @app.route('/')def index():    session['xxx'] = 123    return 'Index'if __name__ == '__main__':    app.run()

   从源码执行情况看,指定为RedisSessionInterface后,它会执行RedisSessionInterface的open_session和save_session方法

def open_session(self, app, request):        #去cookie中获取键为session的值        sid = request.cookies.get(app.session_cookie_name)        if not sid:  #刚开始没有的时候,生成一个随机字符串            sid = self._generate_sid()            #session_class = RedisSession,RedisSession也是一个特殊字典            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')        #到redis里获取 前缀+随机字符串 的值        val = self.redis.get(self.key_prefix + sid)        if val is not None:            try:                #有值就进行反序列化,得到字典                data = self.serializer.loads(val)                #生成redis特殊字典,返回                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)

   在close_session中,其他似曾相识的地方就不看,就看不同的吧

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))        #把序列化后的字符串写入redis,并设置超时时间        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        #最终只是把随机字符串写入到cookie中        response.set_cookie(app.session_cookie_name, session_id,                            expires=expires, httponly=httponly,                            domain=domain, path=path, secure=secure)

 

  除了上述直接指定session_interface类的方式外,还可以通过配置文件的方式进行指定

# 通过配置的方式进行from redis import Redisfrom flask_session import Sessionapp.config['SESSION_TYPE'] = 'redis'app.config['SESSION_REDIS'] = Redis(host='192.168.0.94',port='6379')Session(app)

   我可以看在Session(app)做了一件什么事,实例化对象,执行__init__方法

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

   在这里面执行了init_app方法,对session_interface开始操作

app.session_interface = self._get_interface(app)

   我们大概都能猜到在get_interface(app)里根据配置来为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'])

   最后返回session_interface对象给app.session_interface,过程和第一方式一样的,只不过就多做了一层封装

 

转载于:https://www.cnblogs.com/xinsiwei18/p/9607584.html

你可能感兴趣的文章
让搭建在Github Pages上的Hexo博客可以被Google搜索到
查看>>
Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段...
查看>>
在WPF控件上添加Windows窗口式调整大小行为
查看>>
背水一战 Windows 10 (36) - 控件(弹出类): ToolTip, Popup, PopupMenu
查看>>
打开3389
查看>>
React学习记录
查看>>
nginx常见内部参数,错误总结
查看>>
对象与类
查看>>
《奸的好人2》财色战场----笔记
查看>>
BZOJ 1834网络扩容题解
查看>>
bzoj1878
查看>>
【Vegas原创】Mysql绿色版安装方法
查看>>
Thrift Expected protocol id ffffff82 but got 0
查看>>
分享《去哪儿网》前端笔试题
查看>>
2013-07-04学习笔记二
查看>>
CP15 协处理器寄存器解读
查看>>
【codeforces 787B】Not Afraid
查看>>
【9111】高精度除法(高精度除高精度)
查看>>
【hihocoder 1312】搜索三·启发式搜索(普通广搜做法)
查看>>
JavaFX中ObservableValue类型
查看>>