zoukankan      html  css  js  c++  java
  • flask-session

      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,session
    
    
    app = Flask(__name__)
    app.secret_key = 'suijksdfsd'
    
    # 指定session_interface 类
    from redis import Redis
    from flask_session import RedisSessionInterface
    conn = 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 Redis
    from flask_session import Session
    app.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,过程和第一方式一样的,只不过就多做了一层封装

  • 相关阅读:
    周末之个人杂想(十三)
    PowerTip of the DaySorting Multiple Properties
    PowerTip of the DayCreate Remoting Solutions
    PowerTip of the DayAdd Help to Your Functions
    PowerTip of the DayAcessing Function Parameters by Type
    PowerTip of the DayReplace Text in Files
    PowerTip of the DayAdding Extra Information
    PowerTip of the DayPrinting Results
    Win7下IIS 7.5配置SSAS(2008)远程访问
    PowerTip of the DayOpening Current Folder in Explorer
  • 原文地址:https://www.cnblogs.com/xinsiwei18/p/9607584.html
Copyright © 2011-2022 走看看