找回密码
 立即注册
首页 业界区 安全 flask基础知识深入——会话管理:Flask Session从原生到 ...

flask基础知识深入——会话管理:Flask Session从原生到扩展源码分析及使用

旁拮猾 2 小时前
会话管理:Flask Session从原生到扩展源码分析及使用


目录

  • 会话管理:Flask Session从原生到扩展源码分析及使用

    • 一、Flask 原生Session机制之会话的创建与恢复源码分析
    • 二、原生Session机制之会话的保存与延长会话有效期源码分析及依赖配置
    • 三、flask-session扩展使用


一、Flask 原生Session机制之会话的创建与恢复源码分析


  • 会话管理简述:Flask会话管理的核心是Session机制,它将用户状态信息加密签名后通过Cookie存储在客户端。即:基于Cookie的客户端加密存储
  • 签名的实现:依赖SECRET_KEY完成签名防止篡改
  • 过程描述:当请求到来,会从cookie中获取session会话信息,如果没有会话信息则新创建一个会话,如果有的话就将这个加密签名的session进行签名验证然后进行解密和反序列化还原出Session对象。请求结束时将session转换为一个字典进行序列化进行签名加密,最终将处理后的会话数据通过 Cookie 保存在客户端
  • 源码分析:

    • 请求到来
    1. def wsgi_app(
    2.     self, environ: WSGIEnvironment, start_response: StartResponse
    3. ) -> cabc.Iterable[bytes]:
    4.     """
    5.     1.1 ctx = self.request_context(environ)
    6.             创建请求上下文,且将请求环境数据environ传入
    7.                     即 ctx =  RequestContext(environ)
    8.             通过1.2/1.3处理 ctx有了request/session等属性
    9.        
    10.     """
    11.     ctx = self.request_context(environ)
    12.     try:
    13.         try:
    14.             """
    15.             2.ctx.push()
    16.                     使用RequestContext类的push()方法
    17.               通过2.1/2.2的系列操作完成了将当前上下文设置为活跃的上下文和将ctx中的session最终设置为
    18.               session_class()即SecureCookieSession类的实例对象,使其不在为None而是像字典一样进行会话数据的设置操作
    19.             
    20.             """
    21.             ctx.push()
    22.             
    23.             """
    24.             进行全局钩子执行,路由匹配,视图函数执行,保存Session会话信息等操作
    25.             
    26.             """
    27.             response = self.full_dispatch_request()
    28.             ...
    29.         ctx.pop(error)
    复制代码

    • RequestContext类
    1. class RequestContext:
    2.     def __init__(
    3.         self,
    4.         app: Flask,
    5.         environ: WSGIEnvironment,
    6.         request: Request | None = None,
    7.         session: SessionMixin | None = None,
    8.     ) -> None:
    9.         self.app = app
    10.         """
    11.         1.2 通过1.1操作可见中未传入request则默认request = None,执行request = app.request_class(environ)
    12.                 调用app.request_class()方法并将environ传入
    13.                         即 request = Request(environ)
    14.                         相当于实例化Request类将WSGI服务器中给WSGI应用程序的environ封装到RequestContext类实例对象ctx的                                        request属性中
    15.                         则ctx的request属性中有了请求相关的数据
    16.         """
    17.         if request is None:
    18.             request = app.request_class(environ)
    19.         self.request: Request = request
    20.         
    21.         """
    22.         1.3 同理可见实例化时未设置session的值,此时session为None,则ctx有了session属性只不过现在为None
    23.         
    24.         """
    25.         self.session: SessionMixin | None = session
    26.                 ...
    27.         
    28.     def push(self) -> None:
    29.                 """
    30.                 2.1 设置当前上下文为活跃的上下文操作
    31.                
    32.                 """
    33.                 ...
    34.         
    35.         """
    36.         2.2 上述省略操作完成后:
    37.                 if self.session is None
    38.                         通过1.3操作 可见session为None
    39.                         session_interface = self.app.session_interface
    40.                                 让session_interface等于app的session_interface属性,其属性为一个类的实例对象
    41.                                 即session_interface 为 SecureCookieSessionInterface类的实例对象
    42.                         self.session = session_interface.open_session(self.app, self.request)
    43.                                 此时给session进行从Cookie中获取相关信息
    44.         """
    45.         if self.session is None:
    46.             session_interface = self.app.session_interface
    47.             
    48.             """
    49.             通过2.2.2操作 根据cookie中是否会话信息有来进行判断重新创建一个会话还是用之前的会话,
    50.             若之前cookie中有会话信息,2.2.2操作可见序列化器s对之前序列化的session数据进行了反序列化,以保证数据可以被使                                用
    51.             但无论什么情况session最终是SecureCookieSession类的实例对象,而这个类最终通过层层继承,继承了字典使session
    52.             可以像字典一样进行使用
    53.             
    54.             所以不论cookie中是否有会话信息来判断是否创建新会话还是用之前的会话,2.2.2操作最终保证了session能像字典一样
    55.             进行数据的操作设置。而且session是SecureCookieSession类的实例对象
    56.             
    57.             """
    58.             self.session = session_interface.open_session(self.app, self.request)
    59.             
    60.             """
    61.             由2.2.1决定是否进入,如果进入则表示2.2.1操作时
    62.             调用session_interface即SecureCookieSessionInterface类的get_signing_serializer()方法获取s时检查到
    63.             app.secret_key没有进行设置,导致session为None,从而进入下面的if条件快
    64.             然后继续调用session_interface的make_null_session,实际上是SecureCookieSessionInterface类继承了
    65.             SessionInterface类的make_null_session方法,而这个方法返回一个null_session_class()对象,而
    66.             null_session_class = NullSession为NullSession类
    67.             
    68.             所以如果检查到没有配置secret_key则,此时session为NullSession类的实例对象(会话不可用,但允许只读空会话,可                           是对会话进行设置则报错)
    69.             而这个对象,一旦对session进行操作则进行报错
    70.             
    71.             """
    72.             if self.session is None:
    73.                 self.session = session_interface.make_null_session(self.app)
    复制代码

    • SecureCookieSessionInterface类
    1. class SecureCookieSessionInterface(SessionInterface):
    2.     salt = "cookie-session"
    3.     digest_method = staticmethod(_lazy_sha1)
    4.     key_derivation = "hmac"
    5.     serializer = session_json_serializer
    6.    
    7.     session_class = SecureCookieSession
    8.     def get_signing_serializer(self, app: Flask) -> URLSafeTimedSerializer | None:
    9.         
    10.         if not app.secret_key:
    11.             """
    12.             和open_session方法中
    13.             s = self.get_signing_serializer(app)
    14.                 if s is None:
    15.             return None
    16.             配合
    17.             """
    18.             return None
    19.         keys: list[str | bytes] = []
    20.         if fallbacks := app.config["SECRET_KEY_FALLBACKS"]:
    21.             keys.extend(fallbacks)
    22.         keys.append(app.secret_key)  
    23.         return URLSafeTimedSerializer(
    24.             keys,  
    25.             salt=self.salt,
    26.             serializer=self.serializer,
    27.             signer_kwargs={
    28.                 "key_derivation": self.key_derivation,
    29.                 "digest_method": self.digest_method,
    30.             },
    31.         )
    32.     def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None:
    33.         """
    34.         2.2.1 通过s = self.get_signing_serializer(app)判断是否要进入2.2操作中的分支,即:
    35.                 if self.session is None:
    36.                         self.session = session_interface.make_null_session(self.app)
    37.                        
    38.               即此操作是检查序列化器s能否被正常使用,即检查有没有配置secret_key,以保证后续操作可以正常进行
    39.         """
    40.         s = self.get_signing_serializer(app)
    41.         if s is None:
    42.             return None               
    43.         
    44.         """
    45.         2.2.2
    46.                   如果上面操作中if分支为进入,即表示调用get_signing_serializer()方法得到了URLSafeTimedSerializer
    47.                   类的实例对象,即s
    48.                   val = request.cookies.get(self.get_cookie_name(app))
    49.                           表示通过1.2操作中完成了的request封装了请求相关的数据中获取cookie的信息从中查找会话数据
    50.                   if not val:
    51.                     return self.session_class()
    52.                     表示如果从cookie中获取的会话数据为空则返回一个新会话给2.2中的session,即表示为第一次访问
    53.                     则2.2中session为session_class(),而session_class = SecureCookieSession即表示session为
    54.                     SecureCookieSession类的实例对象
    55.                     而这个类继承CallbackDict类,然而这个个类又继承字典,则表示session可以像字典一样使用来设置信息
    56.         """
    57.         val = request.cookies.get(self.get_cookie_name(app))
    58.         if not val:
    59.             return self.session_class()
    60.         
    61.         """
    62.         设置会话过期时间
    63.         
    64.         """
    65.         max_age = int(app.permanent_session_lifetime.total_seconds())
    66.         
    67.         """
    68.         data = s.loads(val, max_age=max_age)
    69.             如果从获取到了会话信息,即表示不是首次访问,则使用序列化器s的loads方法将会话信息进行反序列化,即将之前保存的序                          列化后的session的数据进行反序列化,并且验证会话是否过期,成功然后则实例化一个SecureCookieSession对象且将
    70.             反序列化后的数据赋值给其属性,最后将这个SecureCookieSession实例化对象返回给到2.2中的session
    71.             失败则创建一个新会话
    72.         """
    73.         try:
    74.             data = s.loads(val, max_age=max_age)
    75.             return self.session_class(data)
    76.         except BadSignature:
    77.             return self.session_class()
    78.         
    79.     def save_session(
    80.         self, app: Flask, session: SessionMixin, response: Response
    81.     ) -> None:
    82.         name = self.get_cookie_name(app)
    83.         domain = self.get_cookie_domain(app)
    84.         path = self.get_cookie_path(app)
    85.         secure = self.get_cookie_secure(app)
    86.         partitioned = self.get_cookie_partitioned(app)
    87.         samesite = self.get_cookie_samesite(app)
    88.         httponly = self.get_cookie_httponly(app)
    89.         if session.accessed:
    90.             response.vary.add("Cookie")
    91.         if not session:
    92.             if session.modified:
    93.                 response.delete_cookie(
    94.                     name,
    95.                     domain=domain,
    96.                     path=path,
    97.                     secure=secure,
    98.                     partitioned=partitioned,
    99.                     samesite=samesite,
    100.                     httponly=httponly,
    101.                 )
    102.                 response.vary.add("Cookie")
    103.             return
    104.         if not self.should_set_cookie(app, session):
    105.             """
    106.             判断会话是否需要保存
    107.             """
    108.             return
    109.         """
    110.         expires = self.get_expiration_time(app, session)
    111.         延长会话的有效期
    112.         val = self.get_signing_serializer(app).dumps(dict(session))
    113.         获取序列化器并且将session转换为真正的字典进行序列化
    114.         response.set_cookie
    115.         将response中的cookie设置会话信息,会话有效期等一些相关信息
    116.         
    117.         """
    118.         expires = self.get_expiration_time(app, session)
    119.         val = self.get_signing_serializer(app).dumps(dict(session))  # type: ignore[union-attr]
    120.         response.set_cookie(
    121.             name,
    122.             val,
    123.             expires=expires,
    124.             httponly=httponly,
    125.             domain=domain,
    126.             path=path,
    127.             secure=secure,
    128.             partitioned=partitioned,
    129.             samesite=samesite,
    130.         )
    131.         response.vary.add("Cookie")
    132.        
    复制代码

    • NullSession类
    1. #类用于在会话不可用时生成更漂亮的错误信息。仍然允许只读空会话,但设置失败。
    2. class NullSession(SecureCookieSession):
    3.     """
    4.     2.2.1 步骤成立即没有配置secret_key,则2.2操作步骤中session为None进入嵌套的if条件快
    5.     然后调用SecureCookieSessionInterface类继承的SessionInterface类的make_null_session()方法,返回一个
    6.     null_session_class()实例对象而这个对象属于 NullSession类
    7.     即:当没有配置secret_key时session为NullSession类的实例对象,而不是SecureCookieSession类的实例对象
    8.    
    9.     即这个类表示不可用会话,没有配置secret_key时session就是这个类的实例话对象
    10.    
    11.     """
    12.     def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
    13.         raise RuntimeError(
    14.             "The session is unavailable because no secret "
    15.             "key was set.  Set the secret_key on the "
    16.             "application to something unique and secret."
    17.         )
    18.     """
    19.     由于这个也继承SecureCookieSession,然后层层导致这个个也可以像字典一样使用
    20.     而下面代码表示当这个类的实例化对象,像字典一样使用后则调用上面的函数进行报错
    21.    
    22.     则表示这个NullSession类实例化对象session为只读会话,而当其进行数据操作时进行报错
    23.    
    24.     """
    25.     __setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail  # noqa: B950
    26.     del _fail
    复制代码
    1.png


二、原生Session机制之会话的保存与延长会话有效期源码分析及依赖配置


  • 预备知识:

    • Vary头:

      • 是HTTP响应头中的重要字段,用于告知缓存服务器(如浏览器),同一URL的响应内容可能应请求头的不同而不同。确保不同请求能拿到相应的响应数据,避免数据的混乱
      • Vary: Cookie头,告知缓存服务器,同一URL的响应内容会因请求头里面的Cookie不同而变化

    • modified : 保存
    • permanent:永久
    • PERMANENT_SESSION_LIFETIME = timedelta(days=31)默认有效期为31天
    • SESSION_REFRESH_EACH_REQUEST = True  与会话能否保存和有效期刷新相关

  • 源码分析

    • SecureCookieSession类
    1. #如果session不是 不可用会话,即不是NullSession类实例化对象。则sessin即是该类的实例化对象
    2. class SecureCookieSession(CallbackDict[str, t.Any], SessionMixin):
    3.         #默认为Fasle,其用来决定会话数据是否需要被序列化并存储到Cookie中
    4.     modified = False
    5.         #默认为Fasle,其用来决定是否需要添加Vary: Cookie头以确保缓存安全
    6.     accessed = False
    7.     def __init__(
    8.         self,
    9.         initial: c.Mapping[str, t.Any] | c.Iterable[tuple[str, t.Any]] | None = None,
    10.     ) -> None:
    11.         """
    12.         **
    13.                 如果会话字典(伪字典)中,包含嵌套字典或者其他可变类型,直接修改这些嵌套对象不会触发该方法
    14.                 即:修改会话字典中嵌套的复杂数据结构(如字典、列表、自定义对象等)的内部内容,而不是直接替换整个嵌套对象
    15.                         不会触发该方法
    16.         **
    17.                 只读操作,只读取会话数据((如session.get('key')或session['key']))也不会触发该方法,只会设置
    18.                 accessed = True
    19.         """
    20.         def on_update(self: te.Self) -> None:
    21.             self.modified = True
    22.             self.accessed = True
    23.         super().__init__(initial, on_update)
    24.         
    25.      #下面表示session如果使用了这些方法进行操作时,即表示会话被访问
    26.     def __getitem__(self, key: str) -> t.Any:
    27.         self.accessed = True
    28.         return super().__getitem__(key)
    29.     def get(self, key: str, default: t.Any = None) -> t.Any:
    30.         self.accessed = True
    31.         return super().get(key, default)
    32.     def setdefault(self, key: str, default: t.Any = None) -> t.Any:
    33.         self.accessed = True
    34.         return super().setdefault(key, default)
    复制代码

    • process_response类
    1.     def process_response(self, response: Response) -> Response:
    2.         #再此之前视图函数执行完成且返回了响应对象
    3.         
    4.         ctx = request_ctx._get_current_object()  
    5.         """
    6.         执行所有after_request 钩子
    7.         
    8.         """
    9.         for func in ctx._after_request_functions:
    10.             response = self.ensure_sync(func)(response)
    11.         for name in chain(request.blueprints, (None,)):
    12.             if name in self.after_request_funcs:
    13.                 for func in reversed(self.after_request_funcs[name]):
    14.                     response = self.ensure_sync(func)(response)
    15.         """
    16.         钩子执行完后
    17.         1.
    18.         if not self.session_interface.is_null_session(ctx.session)
    19.                 调用session_interface的is_null_session方法,实际是SecureCookieSessionInterface类
    20.                 继承的SessionInterface类的is_null_session方法
    21.                
    22.                 即判断session会话是否不是只读会话,即判断sesion是不是NullSession类的实例对象
    23.         """
    24.         if not self.session_interface.is_null_session(ctx.session):
    25.             
    26.             """
    27.             2.
    28.             如果session不是NullSession类的实例对象,即session不是只渎空会话
    29.             即其是SecureCookieSession类的实例对象,然后执行session_interface实例的save_session方法,
    30.             进行session的保存
    31.             
    32.             
    33.             """
    34.             self.session_interface.save_session(self, ctx.session, response)
    35.         """
    36.         通过上述操作将response对象中的cookie得到有最新的会话相关数据,
    37.         后续则将进行把响应发送给客户端
    38.         
    39.         """
    40.         return response
    复制代码

    • SessionInterface类
    1. class SessionInterface:
    2.     null_session_class = NullSession
    3.     pickle_based = False
    4.     def make_null_session(self, app: Flask) -> NullSession:
    5.         """
    6.         创建或恢复会话时,检查到没有配置secret_key后,调用此方法将session设置为只读空会话,即NullSession的实例化对象
    7.         
    8.         """
    9.         return self.null_session_class()
    10.    
    11.     def is_null_session(self, obj: object) -> bool:
    12.         """
    13.         1.1
    14.         1. 中调用了该方法,其用来判断session是不是NullSession的实例化对象,最终决定1.中是否要进行if块内部代码是否
    15.         执行
    16.         
    17.         """
    18.         return isinstance(obj, self.null_session_class)
    19.    
    20.    
    21.     def get_expiration_time(self, app: Flask, session: SessionMixin) -> datetime | None:
    22.         #既然来到这个方法则表明重新设置cookie是肯定的,而有效期是否刷新则要看里面的if session.permanent是否成立
    23.         """
    24.         if session.permanent
    25.                 如果设置了永久会话的配置,则有效期要刷新
    26.                 现在的时间+有效期时间
    27.                 return datetime.now(timezone.utc) + app.permanent_session_lifetime
    28.         
    29.         """
    30.         if session.permanent:
    31.             return datetime.now(timezone.utc) + app.permanent_session_lifetime
    32.         
    33.         """
    34.          session.permanent = False 表示为临时会话,浏览器关闭则下次访问得进行验证。没有有效期一说
    35.         
    36.         """
    37.         return None
    38.    
    39.    
    40.     def should_set_cookie(self, app: Flask, session: SessionMixin) -> bool:
    41.         #决定是否重新设置Cookie以刷新有效期
    42.         """
    43.         2.3.1
    44.                 情况一:
    45.                         如果session.modified为True了,不用看后面条件,表示会话相关数据进行了重新赋值,则2.3中不直接return
    46.                         继续后面操作进行,要进行保存
    47.                 情况二:
    48.                         如果会话数据没有进行重新赋值,且开了永久会话和会话要进行刷新的配置,则2.3中不直接return
    49.                         继续后面操作进行,要进行保存
    50.         
    51.         """
    52.         return session.modified or (
    53.             session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"]
    54.         )
    复制代码

    • SecureCookieSessionInterface类
    1. class SecureCookieSessionInterface(SessionInterface):
    2.    
    3.     def save_session(
    4.         self, app: Flask, session: SessionMixin, response: Response
    5.     ) -> None:
    6.         #获取Cookie配置
    7.         name = self.get_cookie_name(app)
    8.         domain = self.get_cookie_domain(app)
    9.         path = self.get_cookie_path(app)
    10.         secure = self.get_cookie_secure(app)
    11.         partitioned = self.get_cookie_partitioned(app)
    12.         samesite = self.get_cookie_samesite(app)
    13.         httponly = self.get_cookie_httponly(app)
    14.         """
    15.         2.1
    16.                 如果会话被访问,添加vary头:vary:Cookie,告诉缓存服务器,响应内容会因请求头里面的cookie不同而变化
    17.         
    18.         """
    19.         if session.accessed:
    20.             response.vary.add("Cookie")
    21.             
    22.         """
    23.         2.2
    24.                 if not session
    25.                         if session.modified
    26.                                 如果会话数据为空,而且这个空会话是因为进行操作造成的,
    27.                                 表明这个空会话是将原有会话数据进行清空了,
    28.                                 response.delete_cookie()
    29.                                         将客户端中cookie中保存的数据进行删除,
    30.                                         因为原来session有数据,而此时没有数据,表示操作者不想要客户端原有的数据了且不想再存新数据
    31.                                 response.vary.add("Cookie")
    32.                                         添加vary头,告诉缓存服务器,响应内容会因请求头里面的cookie不同而变化,确保不同请求拿到相应的
    33.                                         数据
    34.                                        
    35.                         return
    36.                                 表明,如果会话数据为空,而且这个空会话不是因为进行操作造成的,直接return 不进行无意义的保存。
    37.                                 这针对于,之前创建的新会话而会话没有进行数据的设置,依然为空则不用保存在cookie中直接return
    38.                                
    39.         
    40.         """
    41.         if not session:
    42.             if session.modified:
    43.                 response.delete_cookie(
    44.                     name,
    45.                     domain=domain,
    46.                     path=path,
    47.                     secure=secure,
    48.                     partitioned=partitioned,
    49.                     samesite=samesite,
    50.                     httponly=httponly,
    51.                 )
    52.                 response.vary.add("Cookie")
    53.             return
    54.         
    55.         """
    56.         2.3
    57.                 if not self.should_set_cookie(app, session)
    58.                         判断是否重新设置Cookie以刷新有效期
    59.                         如果2.3.1中是返回return,不论是情况一还是情况二成立均表明重新设置cookie是肯定的,
    60.                         而是否要刷新有效期则看下面代码调用的get_expiration_time方法
    61.         """
    62.         if not self.should_set_cookie(app, session):
    63.             return
    64.         
    65.         """
    66.         2.4
    67.         expires = self.get_expiration_time(app, session)
    68.         进行会话是否能够延长有效期判断并延长有效期计算
    69.         
    70.         """
    71.         expires = self.get_expiration_time(app, session)
    72.         
    73.         
    74.         """
    75.         2.5
    76.         将session转换成真正的字典,用签名序列化器进行序列化
    77.         
    78.         """
    79.         val = self.get_signing_serializer(app).dumps(dict(session))  
    80.         
    81.         
    82.         """
    83.         2.6
    84.         将cookie中获取的配置、session中保存的数据、会话有效期时间等放到cookie中,
    85.         添加响应头vary字段设置为cookie,告诉缓存服务器,响应的内容随请求头中cookie的不同而不同,确保同一URL下的不同请求
    86.         能获取对应的响应,确保客户端的cookie能够得到最新的数据以便下一次的会话正常进行
    87.         
    88.         """
    89.         response.set_cookie(
    90.             name,
    91.             val,
    92.             expires=expires,
    93.             httponly=httponly,
    94.             domain=domain,
    95.             path=path,
    96.             secure=secure,
    97.             partitioned=partitioned,
    98.             samesite=samesite,
    99.         )
    100.         response.vary.add("Cookie")
    复制代码
  • 会话保存与有效期延期依赖配置
    通过上面源码可知:

    • 会话是否要保存取决于SessionInterface类的should_set_cookie方法

      • 如果session.modified = True 则会话数据会进行保存

        • 由于session是SecuresCookieSession的实例化对象而其中类属性session.modified默认为False
        • session.modified = True触发条件

          • 会话字典中嵌套的复杂数据结构(如字典、列表、自定义对象等)修改其复杂数据结构的内部内容,而不是直接替换整个嵌套对象不会触发SessionInterface类的on_update方法,即不会触发将其改为True
          • 只访问会话字典的数据时,也不会触上述该方法。只会将accessed改为True


      • 如果不为True则须看session.permanent = True和app.config["SESSION_REFRESH_EACH_REQUEST"] = True是否同时成立,一般源码中默认app.config["SESSION_REFRESH_EACH_REQUEST"] = True,而默认session.permanent = False

    • 会话有效长能否延期取决于会话是否要保存和SecureCookieSessionInterface类继承的SessionInterface类的get_expiration_time方法

      • 如果session.permanent = True则刷新会话有效期

    • 总结:
      通过上述可知,会话有效期能否进行刷新首先取决于会话保存与否,再取决于session.permanent

      • 会话保存与否依赖(两种情况能够进行保存):

        • session.modified
        • session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"]

      • 会话能否进行有效期刷新依赖(两种情况可进行会话有效期刷新):

        • session.permanent = True and session.modified = True
          app.config["SESSION_REFRESH_EACH_REQUEST"]任意值
        • session.permanent = True and app.config["SESSION_REFRESH_EACH_REQUEST"] = True
          session.modified任意值
        • 即:session.permanent = True and  session.modified = True  or app.config["SESSION_REFRESH_EACH_REQUEST"] = True



2.png

三、flask-session扩展使用


  • 用途:将原生保存在Cookie中的session,保存在redis或memcached等指定的地方
  • 使用分析:由上述源码可见,原生session保存在Cookie中一切源于:
    session_interface = self.app.session_interface即:session_interface = SecureCookieSessionInterface
  • 使用方法(Redis):与保存在Cookie不同,redis中默认session.permanent = True,其他大体一致

    • 源码内容:
    1. class Session:
    2.     def __init__(self, app=None):
    3.         self.app = app
    4.         if app is not None:
    5.             self.init_app(app)
    6.     def init_app(self, app):
    7.         app.session_interface = self._get_interface(app)
    8.         
    9.     def _get_interface(self, app):
    10.             config = app.config
    11.             SESSION_TYPE = config.get("SESSION_TYPE", Defaults.SESSION_TYPE)
    12.         ...
    13.         
    14.         # Redis settings
    15.         SESSION_REDIS = config.get("SESSION_REDIS", Defaults.SESSION_REDIS)
    16.         
    17.         
    18.                 if SESSION_TYPE == "redis":
    19.                         from .redis import RedisSessionInterface
    20.                         session_interface = RedisSessionInterface(
    21.                                 **common_params,
    22.                                 client=SESSION_REDIS,
    23.                         )
    24.          elif:
    25.                 ...
    26.          
    27.         ...
    28.         
    29.         return session_interface
    30.         
    31.         
    复制代码

    • 方法:
    1. from flask import Flask, session
    2. from flask_session import Session
    3. import redis
    4. app = Flask(__name__)
    5. # 1. 配置Redis连接
    6. app.config['SESSION_TYPE'] = 'redis'  # 指定Session存储类型为Redis
    7. app.config['SESSION_REDIS'] = redis.Redis(
    8.     host='127.0.0.1',  # Redis服务器地址
    9.     port=6379,         # Redis端口
    10.     # password='xxx',  # 若Redis有密码则添加
    11.     db=0               # 使用第0个数据库
    12. )
    13. # 2. 可选配置(按需加)
    14. app.config['SECRET_KEY'] = 'your_secret_key_here'  # 必须设置,用于Session加密
    15. app.config['SESSION_PERMANENT'] = True  # Session是否持久化(默认True,关闭则设为False)
    16. app.config['PERMANENT_SESSION_LIFETIME'] = 3600  # 持久化Session的有效期(秒,默认31天)
    17. app.config['SESSION_USE_SIGNER'] = True  # 是否对SessionID签名(防止篡改,建议开启)
    18. # 3. 初始化Session
    19. Session(app)
    复制代码

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册