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.pswp.cn/news/390372.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/390372.shtml
英文地址,請注明出處:http://en.pswp.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…