内置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. vs2010更新EF模型时报错

    尝试从数据库进行更新时,遇到类型为“Microsoft.VSDesigner.Data.Local.ConnectionStringConverterServiceException”的异常.异常消息 ...

  2. 【基础练习】【BFS+A*】codevs1225八数码难题题解

    题目描写叙述 Description Yours和zero在研究A*启示式算法.拿到一道经典的A*问题,可是他们不会做,请你帮他们. 问题描写叙述 在3×3的棋盘上,摆有八个棋子,每一个棋子上标有1至 ...

  3. 360破解大赛crackme分析--之3DES解密附加数据

    具体的分析这里有.本人仅仅是对这里面有趣的算法进行了一些学习 分析链接 这次是逆向的使用3DES解密的过程中的内容: 使用微软的crypt库 使用3DES解密程序中的附加数据 代码: VOID enc ...

  4. 常用的jQuery前端技巧收集

    调试时巧用console.log(),这比用alert()方便多了. jquery易错点:元素拼接的时候,元素还未添加到DOM,就用该预添加元素操作. ajax动态获取的数据,还没有装载html元素, ...

  5. U盘重装Windows系统

    1.制作一个U盘老毛桃或者大白菜 2.进入BIOS 3.Secure Boot-Disabled,作用是关闭微软的Secure BOOT,这个功能开启会导致不能识别U盘启动系统的安装 4.Lauch ...

  6. 应用开发之Linq和EF

    本章简言 上一章笔者对于WinForm开发过程用到的几个知识点做了讲解.笔者们可以以此为开端进行学习.而本章我们来讲一个跟ORM思想有关的知识点.在讲之前让我们想一下关于JAVA的hibernate知 ...

  7. 指针与C++基本原理

    面向对象编程与传统的过程性编程的区别在于,OOP强调的是在运行阶段(而不是编译阶段)进行决策.运行阶段指的是程序正在运行时,编译阶段指的是编译器将程序组合起来时.运行阶段决策就好比度假时,选择参观那些 ...

  8. Windows电脑键盘快捷键大全【最全的快捷键】

    Windows电脑键盘快捷键大全[最全的快捷键] 一.常见用法: F1显示当前程序或者windows的帮助内容. F2当你选中一个文件的话,这意味着“重命名” F3当你在桌面上的时候是打开“查找:所有 ...

  9. 1358 棋盘游戏[状压dp]

    1358 棋盘游戏  时间限制: 1 s  空间限制: 64000 KB  题目等级 : 大师 Master 题解  查看运行结果     题目描述 Description 这个游戏在一个有10*10 ...

  10. 在 Java 应用程序中绑定 Bean 和数据

    本指南介绍了 NetBeans IDE 对 Java 应用程序中 Bean 绑定和数据绑定的支持. 要学完本教程,您需要具备以下软件和资源. 软件或资源 要求的版本 NetBeans IDE 版本 7 ...