• 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,过程和第一方式一样的,只不过就多做了一层封装

  • 相关阅读:
    日常练习-利用python的random模块模拟身份证号码
    学习笔记-redis
    学习笔记-AJAX&JSON
    学习笔记-JQuery
    学习笔记-Filter&Listener
    学习笔记-EL&JSTL
    学习笔记-Cookie&Session
    学习笔记-Response
    学习笔记-XML
    JToken中并没有Value这个属性,但在运行时可以看到,用dyna可以取到这个属性值
  • 原文地址:https://www.cnblogs.com/xinsiwei18/p/9607584.html
Copyright © 2020-2023  润新知