内置session原理

请求到来

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

def wsgi_app(self, environ, start_response):
# 获取请求相关数据,并进行封装和加工
ctx = self.request_context(environ)
# 将请求消息推送到堆栈中,并执行 open_session方法
ctx.push()
error = None
try:
try:
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.make_response(self.handle_exception(e))
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.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.top
if 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.top
if 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 all
session data in a signed cookie. This requires that the
:attr:`secret_key` is set. Instead of overriding this method
we 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 cookies
through 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 sha1
digest_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_serializer
session_class = SecureCookieSession def get_signing_serializer(self, app):
if not app.secret_key:
return None
signer_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就是None
s = 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 expected
by 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 = True class UpdateDictMixin(object): """Makes dicts call `self.on_update` on modifications. .. versionadded:: 0.5 :private:
""" on_update = None def 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 rv
oncall.__name__ = name
return oncall def setdefault(self, key, default=None):
modified = key not in self
rv = super(UpdateDictMixin, self).setdefault(key, default)
if modified and self.on_update is not None:
self.on_update(self)
return rv def pop(self, key, default=_missing):
modified = key in self
if 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_update class 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_update def __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 = True
CallbackDict.__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 a
reference 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 and
can continue to call methods on it. .. versionchanged:: 0.7
The behavior of the before and after request callbacks was changed
under error conditions and a new callback was added that will
always execute at the end of the request, independent on if an
error 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 optional
exception context to start the response
"""
ctx = self.request_context(environ)
ctx.push()
error = None
try:
try:
# 处理业务请求,并获取返回值
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.make_response(self.handle_exception(e))
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
 def full_dispatch_request(self):
"""Dispatches the request and on top of that performs request
pre and postprocessing as well as HTTP exception catching and
error 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 object
before it's sent to the WSGI server. By default this will
call all the :meth:`after_request` decorated functions. .. versionchanged:: 0.5
As of Flask 0.5 the functions registered for after request
execution 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 an
instance of :attr:`response_class`.
"""
ctx = _request_ctx_stack.top
bp = ctx.request.blueprint
funcs = ctx._after_request_functions
if 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 response def save_session(self, session, response):
"""Saves the session if it needs updates. For the default
implementation, check :meth:`open_session`. Instead of overriding this
method 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 cookies
through 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 sha1
digest_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_serializer
session_class = SecureCookieSession def get_signing_serializer(self, app):
if not app.secret_key:
return None
signer_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 None
val = 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):
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))
response.set_cookie(app.session_cookie_name, val,
expires=expires, httponly=httponly,
domain=domain, path=path, secure=secure)

flask内置session原理的更多相关文章

  1. Flask - 内置Session

    目录 Flask - 内置Session 基本用法 给视图添加装饰器验证 Flask - 内置Session Flask中的Session非常的奇怪,他会将你的SessionID存放在客户端的Cook ...

  2. python 全栈开发,Day119(Flask初识,Render Redirect HttpResponse,request,模板语言 Jinja2,用户登录例子,内置Session)

    一.Flask初识 首先,要看你学没学过Django 如果学过Django 的同学,请从头看到尾,如果没有学过Django的同学,并且不想学习Django的同学,轻饶过第一部分 三大主流Web框架对比 ...

  3. Flask内置URL变量转换器

    Flask内置URL变量转换器: 转换器通过特定的规则执行,”<转换器: 变量名>”.<int: year>把year的值转换为证书,因此我们可以在视图函数中直接对year变量 ...

  4. flask内置的信号

    from flask import Flask,request,template_rendered,render_template,got_request_exception from signals ...

  5. 初识Flask框架,以及Flask中的模板语言jinjia2和Flask内置的Session

    一.web框架的对比 首先我们先来看下比较火的web框架 1.Django: 优点:大而全,所有组件都是组织内部开发高度定制化,教科书级别的框架 缺点:大到浪费资源,请求的时候需要的资源较高 2.Fl ...

  6. Flask中的内置session

    Flask中的Session非常的奇怪,他会将你的SessionID存放在客户端的Cookie中,使用起来也非常的奇怪 1. Flask 中 session 是需要 secret_key 的 from ...

  7. flask 第六篇 flask内置的session

    Flask中的Session非常的奇怪,他会将你的SessionID存放在客户端的Cookie中,使用起来也非常的奇怪 1. Flask 中 session 是需要 secret_key 的 from ...

  8. flask基础之session原理详解(十)

    前言 flask_session是flask框架实现session功能的一个插件,用来替代flask自带的session实现机制,flask默认的session信息保存在cookie中,不够安全和灵活 ...

  9. Flask内置命令行工具—CLI

    应用发现 flask命令在Flask库安装后可使用,使用前需要正确配置FLASK_APP环境变量以告知用户程序所在位置.不同平台设置方式有所不同. Unix Bash (Linux, Mac, etc ...

随机推荐

  1. 系统之间通讯方式—SOAP(web service)

    [web service 简介] 是一种新的web应用程序分支,是自包含.自描述.模块化的应用,可以发布.定位.通过web调用.web service是一个应用组件,它逻辑性的为其他应用程序提供数据与 ...

  2. iOS开发之--最简单的导航按钮更换方法/导航颜色的改变

    有很多时候,我们需要用到导航,那么更换导航的时候,是在那用那修改,还是自定义一个导航,或者是声明一个代理方法,经过查资料和对导航属性的一些了解,用一种方法最为简单,就是在入口类里面添加一个方法,调用偏 ...

  3. 如何用C语言读写文件

    #include "stdio.h"#include <stdlib.h> main(){ FILE *fp1;//定义文件流指针,用于打开读取的文件 FILE *fp ...

  4. ArcGIS Android SDK 中文标注乱码

    Android使用如下代码添加标注: TextSymbol ts = new TextSymbol(12, "名称", Color.RED);Graphic gp = new Gr ...

  5. Android 安卓真机调试 出现Installation error: INSTALL_FAILED_UPDATE_INCOMPATIBLE....

    [2017-03-24 13:30:04 - DataVDemo06] Installing DataVDemo06.apk...[2017-03-24 13:30:08 - DataVDemo06] ...

  6. Zabbix-3.0.3使用自带模板监控MySQL

    导读 Zabbix是一款优秀的,开源的,企业级监控软件,可以通过二次开发来监控你想要监控的很多服务,本文介绍使用Zabbix自带的模板监控MySQL服务. 配置userparameter_mysql. ...

  7. js 一些基础知识

    数据类型: 作用域 每个函数都有自己的执行环境,执行环境定义了变量有权访问的其他数据,决定了他们各自的行为. 每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有 ...

  8. ios开发 更改状态栏

    设置statusBar 简单来说,就是设置显示电池电量.时间.网络部分标示的颜色, 这里只能设置两种颜色: 默认的黑色(UIStatusBarStyleDefault) 白色(UIStatusBarS ...

  9. mysql 标点符号

    w攻防一体化.

  10. the age of the TCP connection TCP Slow Start

    w防止网络过载和拥塞 HTTP The Definitive Guide The performance of TCP data transfer also depends on the age of ...