flask内置session原理

内置session原理

请求到来

当请求进来之后,先执行Flask对象的 __call__ 方法

def wsgi_app(self, environ, start_response):# 获取请求相关数据,并进行封装和加工ctx = self.request_context(environ)# 将请求消息推送到堆栈中,并执行 open_session方法
        ctx.push()error = Nonetry:try:response = self.full_dispatch_request()except Exception as e:error = eresponse = self.make_response(self.handle_exception(e))return response(environ, start_response)finally:if self.should_ignore_error(error):error = Nonectx.auto_pop(error)def __call__(self, environ, start_response):"""Shortcut for :attr:`wsgi_app`."""return self.wsgi_app(environ, start_response)
 def push(self):top = _request_ctx_stack.topif top is not None and top.preserved:top.pop(top._preserved_exc)# Before we push the request context we have to ensure that there# is an application context.app_ctx = _app_ctx_stack.topif app_ctx is None or app_ctx.app != self.app:app_ctx = self.app.app_context()app_ctx.push()self._implicit_app_ctx_stack.append(app_ctx)else:self._implicit_app_ctx_stack.append(None)if hasattr(sys, 'exc_clear'):sys.exc_clear()_request_ctx_stack.push(self)# 调用Flask对象的open_session方法self.session = self.app.open_session(self.request)if self.session is None:self.session = self.app.make_null_session()
 def open_session(self, request):"""Creates or opens a new session.  Default implementation stores allsession data in a signed cookie.  This requires that the:attr:`secret_key` is set.  Instead of overriding this methodwe recommend replacing the :class:`session_interface`.:param request: an instance of :attr:`request_class`."""# self指的是Flask对象,session_interface默认值为SecureCookieSessionInterface()return self.session_interface.open_session(self, request)

由以上源码发现,当接收到用户请求之后,会调用 Flask对象的 session_interface对象的open_session方法,以此来获取一个session对象。

class SecureCookieSessionInterface(SessionInterface):"""The default session interface that stores sessions in signed cookiesthrough the :mod:`itsdangerous` module."""#: the salt that should be applied on top of the secret key for the#: signing of cookie based sessions.salt = 'cookie-session'#: the hash function to use for the signature.  The default is sha1digest_method = staticmethod(hashlib.sha1)#: the name of the itsdangerous supported key derivation.  The default#: is hmac.key_derivation = 'hmac'#: A python serializer for the payload.  The default is a compact#: JSON derived serializer with support for some extra Python types#: such as datetime objects or tuples.serializer = session_json_serializersession_class = SecureCookieSessiondef get_signing_serializer(self, app):if not app.secret_key:return Nonesigner_kwargs = dict(key_derivation=self.key_derivation,digest_method=self.digest_method)return URLSafeTimedSerializer(app.secret_key, salt=self.salt,serializer=self.serializer,signer_kwargs=signer_kwargs)def open_session(self, app, request):# 获取加密相关的类,必须设置app.secret_key,不然s就是Nones = self.get_signing_serializer(app)if s is None:return None# 去Cookie中获取 session 对应的值(该值默认是加密之后的session的值,也可以改造成随机字符串)val = request.cookies.get(app.session_cookie_name)if not val:# 未获取到值,则创建一个空字典(就是flask中用到的session)return self.session_class()max_age = total_seconds(app.permanent_session_lifetime)try:data = s.loads(val, max_age=max_age)# 如果获取到值,则将值放入字典中(就是flask中用到的session)return self.session_class(data)except BadSignature:# 解密失败,则创建一个空字典(就是flask中用到的session)return self.session_class()

上述中 self.session_class 就是创建的一个SecureCookieSession对象,这个类是继承了字典的类,其实就是一个特殊的字典。

class SessionMixin(object):"""Expands a basic dictionary with an accessors that are expectedby Flask extensions and users for the session."""def _get_permanent(self):return self.get('_permanent', False)def _set_permanent(self, value):self['_permanent'] = bool(value)#: this reflects the ``'_permanent'`` key in the dict.permanent = property(_get_permanent, _set_permanent)del _get_permanent, _set_permanent#: some session backends can tell you if a session is new, but that is#: not necessarily guaranteed.  Use with caution.  The default mixin#: implementation just hardcodes ``False`` in.new = False#: for some backends this will always be ``True``, but some backends will#: default this to false and detect changes in the dictionary for as#: long as changes do not happen on mutable structures in the session.#: The default mixin implementation just hardcodes ``True`` in.modified = Trueclass UpdateDictMixin(object):"""Makes dicts call `self.on_update` on modifications... versionadded:: 0.5:private:"""on_update = Nonedef calls_update(name):def oncall(self, *args, **kw):rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw)if self.on_update is not None:self.on_update(self)return rvoncall.__name__ = namereturn oncalldef setdefault(self, key, default=None):modified = key not in selfrv = super(UpdateDictMixin, self).setdefault(key, default)if modified and self.on_update is not None:self.on_update(self)return rvdef pop(self, key, default=_missing):modified = key in selfif default is _missing:rv = super(UpdateDictMixin, self).pop(key)else:rv = super(UpdateDictMixin, self).pop(key, default)if modified and self.on_update is not None:self.on_update(self)return rv__setitem__ = calls_update('__setitem__')__delitem__ = calls_update('__delitem__')clear = calls_update('clear')popitem = calls_update('popitem')update = calls_update('update')del calls_updateclass CallbackDict(UpdateDictMixin, dict):"""A dict that calls a function passed every time something is changed.The function is passed the dict instance."""def __init__(self, initial=None, on_update=None):dict.__init__(self, initial or ())self.on_update = on_updatedef __repr__(self):return '<%s %s>' % (self.__class__.__name__,dict.__repr__(self))class SecureCookieSession(CallbackDict, SessionMixin):"""Base class for sessions based on signed cookies."""def __init__(self, initial=None):def on_update(self):self.modified = TrueCallbackDict.__init__(self, initial, on_update)self.modified = False

该字典其实就是继承了字典,并在其基础上定制了一些功能,如

class MyDict(dict):def __init__(self, initial):dict.__init__(self, initial)session = MyDict({'k1': 123})print(session, type(session)) # {'k1': 123} <class '__main__.MyDict'>
session['k2'] = 'v2'
print(session)

所以,Flask的视图函数中在对session进行操作时,其实就是在内存中修改一个字典的数据。

业务处理

设置session

响应内容

响应内容其实就讲数据返回给用户,并且把内容中的session重新保存

def wsgi_app(self, environ, start_response):"""The actual WSGI application.  This is not implemented in`__call__` so that middlewares can be applied without losing areference to the class.  So instead of doing this::app = MyMiddleware(app)It's a better idea to do this instead::app.wsgi_app = MyMiddleware(app.wsgi_app)Then you still have the original application object around andcan continue to call methods on it... versionchanged:: 0.7The behavior of the before and after request callbacks was changedunder error conditions and a new callback was added that willalways execute at the end of the request, independent on if anerror occurred or not.  See :ref:`callbacks-and-errors`.:param environ: a WSGI environment:param start_response: a callable accepting a status code,a list of headers and an optionalexception context to start the response"""ctx = self.request_context(environ)ctx.push()error = Nonetry:try:# 处理业务请求,并获取返回值response = self.full_dispatch_request()except Exception as e:error = eresponse = self.make_response(self.handle_exception(e))return response(environ, start_response)finally:if self.should_ignore_error(error):error = Nonectx.auto_pop(error)
 def full_dispatch_request(self):"""Dispatches the request and on top of that performs requestpre and postprocessing as well as HTTP exception catching anderror handling... versionadded:: 0.7"""self.try_trigger_before_first_request_functions()try:request_started.send(self)# 执行视图函数,处理业务请求rv = self.preprocess_request()if rv is None:rv = self.dispatch_request()except Exception as e:rv = self.handle_user_exception(e)response = self.make_response(rv)# 处理响应内容response = self.process_response(response)request_finished.send(self, response=response)return response
def process_response(self, response):"""Can be overridden in order to modify the response objectbefore it's sent to the WSGI server.  By default this willcall all the :meth:`after_request` decorated functions... versionchanged:: 0.5As of Flask 0.5 the functions registered for after requestexecution are called in reverse order of registration.:param response: a :attr:`response_class` object.:return: a new response object or the same, has to be aninstance of :attr:`response_class`."""ctx = _request_ctx_stack.topbp = ctx.request.blueprintfuncs = ctx._after_request_functionsif bp is not None and bp in self.after_request_funcs:funcs = chain(funcs, reversed(self.after_request_funcs[bp]))if None in self.after_request_funcs:funcs = chain(funcs, reversed(self.after_request_funcs[None]))for handler in funcs:response = handler(response)if not self.session_interface.is_null_session(ctx.session):# 执行flask对象的save_session方法
            self.save_session(ctx.session, response)return responsedef save_session(self, session, response):"""Saves the session if it needs updates.  For the defaultimplementation, check :meth:`open_session`.  Instead of overriding thismethod we recommend replacing the :class:`session_interface`.:param session: the session to be saved (a:class:`~werkzeug.contrib.securecookie.SecureCookie`object):param response: an instance of :attr:`response_class`"""# 执行session_interface的save_session方法,将内存中的session保存。return self.session_interface.save_session(self, session, response)

执行xxx的save_session方法,将内存中的数据保存。

 

class SecureCookieSessionInterface(SessionInterface):"""The default session interface that stores sessions in signed cookiesthrough the :mod:`itsdangerous` module."""#: the salt that should be applied on top of the secret key for the#: signing of cookie based sessions.salt = 'cookie-session'#: the hash function to use for the signature.  The default is sha1digest_method = staticmethod(hashlib.sha1)#: the name of the itsdangerous supported key derivation.  The default#: is hmac.key_derivation = 'hmac'#: A python serializer for the payload.  The default is a compact#: JSON derived serializer with support for some extra Python types#: such as datetime objects or tuples.serializer = session_json_serializersession_class = SecureCookieSessiondef get_signing_serializer(self, app):if not app.secret_key:return Nonesigner_kwargs = dict(key_derivation=self.key_derivation,digest_method=self.digest_method)return URLSafeTimedSerializer(app.secret_key, salt=self.salt,serializer=self.serializer,signer_kwargs=signer_kwargs)def open_session(self, app, request):s = self.get_signing_serializer(app)if s is None:return Noneval = request.cookies.get(app.session_cookie_name)if not val:return self.session_class()max_age = total_seconds(app.permanent_session_lifetime)try:data = s.loads(val, max_age=max_age)return self.session_class(data)except BadSignature:return self.session_class()def save_session(self, app, session, response):domain = self.get_cookie_domain(app)path = self.get_cookie_path(app)# Delete case.  If there is no session we bail early.# If the session was modified to be empty we remove the# whole cookie.if not session:if session.modified:response.delete_cookie(app.session_cookie_name,domain=domain, path=path)return# Modification case.  There are upsides and downsides to# emitting a set-cookie header each request.  The behavior# is controlled by the :meth:`should_set_cookie` method# which performs a quick check to figure out if the cookie# should be set or not.  This is controlled by the# SESSION_REFRESH_EACH_REQUEST config flag as well as# the permanent flag on the session itself.if not self.should_set_cookie(app, session):returnhttponly = 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))response.set_cookie(app.session_cookie_name, val,expires=expires, httponly=httponly,domain=domain, path=path, secure=secure)

 

转载于:https://www.cnblogs.com/ctztake/p/8260887.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/390372.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

指针3

#include <stdio.h>/* 2018-05-28 如何通过被调函数修改主调函数普通变量的值1&#xff0c;实参必须为该普通变量的地址2,形参必须为指针变量3&#xff0c;在背调函数中通过*形参名 。。。。。的方式就可以修改主调函数相关变量的值*/f(int *i,int *j) {*i 4;*j 5;ret…

面试系统设计_系统设计面试问题–您应该知道的概念

面试系统设计You may have heard the terms "Architecture" or "System Design." These come up a lot during developer job interviews – especially at big tech companies.您可能已经听说过“架构”或“系统设计”这两个术语。 在开发人员工作面试中&…

8597 石子划分问题 dpdp,只考虑第一次即可

8597 石子划分问题 时间限制:500MS 内存限制:1000K提交次数:155 通过次数:53 题型: 编程题 语言: G;GCC;VC Description 给定n个石子&#xff0c;其重量分别为a1,a2,a3,...,an。 要求将其划分为m份&#xff0c;每一份的划分费用定义为这份石子中最大重量与最小重量差的平方。…

文章中嵌入代码块_如何在您的文章中嵌入多项选择测验问题

文章中嵌入代码块In my experience, supplementing study with practical exercises greatly improves my understanding of a topic. This is especially true when I can test my knowledge as I go and receive instant feedback for each question.以我的经验&#xff0c;通…

mysql免安装版配置

1.官网下载https://dev.mysql.com/downloads/mysql/ 2.将下载好的压缩包mysql-5.7.20-winx64.zip解压。 3.mysql解压后&#xff0c;设置.ini文件&#xff0c;在加压后的路径中加一个my.ini文件 配置如下内容&#xff1a; # 设置mysql客户端默认字符集 default-character-setutf…

各种IE(IE6-IE10)兼容问题一行代码搞定

x-ua-compatible 用来指定IE浏览器解析编译页面的model x-ua-compatible 头标签大小写不敏感&#xff0c;必须用在 head 中&#xff0c;必须在除 title 外的其他 meta 之前使用。 1、使用一行代码来指定浏览器使用特定的文档模式。 <meta http-equiv"x-ua-compatible&q…

802. 找到最终的安全状态

在有向图中&#xff0c;以某个节点为起始节点&#xff0c;从该点出发&#xff0c;每一步沿着图中的一条有向边行走。如果到达的节点是终点&#xff08;即它没有连出的有向边&#xff09;&#xff0c;则停止。 对于一个起始节点&#xff0c;如果从该节点出发&#xff0c;无论每…

元类型与类型的区别

元类型是指所有类型的类型。 元类型只能类型出现在类型标示位&#xff1b; 类型即能作为类型存在&#xff0c;出现在类型标示位&#xff1b; 也能作为变量存在&#xff0c;出现在元类型的变量位。 http://www.swift51.com/swift2.0/chapter3/03_Types.html#type_inheritance_cl…

css 动画使用_如何在CSS中使用动画

css 动画使用使用CSS动画 (Using CSS Animations) CSS animations add beauty to the webpages and make transitions from one CSS style to the other beautiful.CSS动画可以使网页更加美观&#xff0c;并可以从一种CSS样式过渡到另一种CSS样式。 To create a CSS animation…

第01章—快速构建

spring boot 系列学习记录&#xff1a;http://www.cnblogs.com/jinxiaohang/p/8111057.html 码云源码地址&#xff1a;https://gitee.com/jinxiaohang/springboot 一、Spring Initializr 使用教程 &#xff08;IntelliJ IDEA&#xff09; 具体步骤&#xff1a; 1、打开IDEA &am…

看板和scrum_看板VS Scrum-如何变得敏捷

看板和scrumScrum and Kanban are the two most popular project management techniques today in business. As a developer, I think its important to understand these processes as you will likely be heavily involved in them if you are part of a team. By understan…

JS之Promise

开胃菜&#xff0c;先做如下思考&#xff1a; Promise 有几种状态&#xff1f;Promise 状态之间可以转化吗&#xff1f;Promise 中的异常可以被 try...catch 捕获吗&#xff1f;Promise前世 callback hell 大家都知道JS是异步操作&#xff08;执行&#xff09;的&#xff0c;在…

鱼眼镜头的distortion校正【matlab】

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 作者&#xff1a;WWC %%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% 功能&#xff1a;畸变矫正 clc; clear; close all; %% 读取图像 Aimread(D:\文件及下载相关\图片\distortion2.jpg)…

web后端开发学习路线_学习后端Web开发的最佳方法

web后端开发学习路线My previous article described how you can get into frontend development. It also discussed how the front end can be a place filled with landmines – step in the wrong place and youll be overwhelmed by the many frameworks of the JavaScrip…

C# 使用WinApi操作剪切板Clipboard

前言&#xff1a; 最近正好写一个程序&#xff0c;需要操作剪切板 功能很简单&#xff0c;只需要从剪切板内读取字符串&#xff0c;然后清空剪切板&#xff0c;然后再把字符串导入剪切板 我想当然的使用我最拿手的C#来完成这项工作&#xff0c;原因无他&#xff0c;因为.Net框架…

聊聊spring cloud gateway的XForwardedHeadersFilter

序 本文主要研究spring cloud gateway的XForwardedHeadersFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC1-sources.jar!/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java Configuration ConditionalOnProperty(name "sp…

node缓冲区_Node.js缓冲区介绍

node缓冲区什么是缓冲液&#xff1f; (What are Buffers?) Binary is simply a set or a collection of 1 and 0. Each number in a binary, each 1 and 0 in a set are called a bit. Computer converts the data to this binary format to store and perform operations. Fo…

专访赵加雨:WebRTC在网易云信的落地

去年的这个时候&#xff0c;在市面上公开表示使用WebRTC的公司还没几家&#xff0c;但2018年以来&#xff0c;宣布采用或支持WebRTC的公司已经越来越多。实时音视频提供商网易云信也在自研的NRTC中集成了WebRTC。在他们眼里&#xff0c;2017年是WebRTC的转折之年&#xff0c;而…

html/css杂题

1、css选择器&#xff1a;详细&#xff08;http://www.ruanyifeng.com/blog/2009/03/css_selectors.html&#xff09; 派生选择器&#xff1a;按标签 类别选择器&#xff1a;按class ID选择器&#xff1a;按ID 通用选择器&#xff1a;* 匹配所有 属性选择器&#xff1a;按属性&…

黑客马拉松 招募_我如何赢得第一次黑客马拉松-研究,设计和编码的2个狂野日子

黑客马拉松 招募I had no coding or engineering background. I studied biology in college, with no clue about what to do with my degree. 我没有编码或工程背景。 我在大学学习生物学&#xff0c;但不知道如何处理我的学位。 My first jobs were making cold calls in s…