flask源码解析之session
内容回顾
cookie与session的区别:
1. session 是保存在服务端的键值对
2. cookie 只能保存4096个字节的数据,但是session不受限制
3. cookie保存在浏览器,安全性差,但是session的安全性高
4. 通过cookie 识别浏览器,获取cookie中的随机序列从session中获取该用户的相关数据,解决了http无状态的缺点
源码流程
1. session的"生成"
在 RequestContext 类中
def __init__(self, app, environ, request=None):
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = app.create_url_adapter(self.request)
self.flashes = None
self.session = None
2. 在 app.wsgi_app()函数中
2.1 ctx.push()
2.2 将生成的 SecureCookieSession类实例赋值个app.session
def push(self):
"""Binds the request context to the current context."""
# If an exception occurs in debug mode or if context preservation is
# activated under exception situations exactly one context stays
# on the stack. The rationale is that you want to access that
# information under debug situations. However if someone forgets to
# pop that context again we want to make sure that on the next push
# it's invalidated, otherwise we run at risk that something leaks
# memory. This is usually only a problem in test suite since this
# functionality is not active in production environments.
# 获取到的 top == ctx
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.
"""
_request_ctx_stack 和 _app_ctx_stack 都是 Local 类的实例
"""
# 获取 应用上下文的栈顶元素,得到 app_ctx
app_ctx = _app_ctx_stack.top
if app_ctx is None or app_ctx.app != self.app:
# self.app == Fask()
# 得到 一个 AppContext类的实例对象,得到一个 应用上下文对象 app_ctx,此时 app_ctx拥有以下属性: app_ctx.app = app, app_ctx.g = app.app_ctx_globals_class()
app_ctx = self.app.app_context()
# 将 app_ctx 入栈,应用上下文入栈
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() # self 指的是 ctx,即将ctx入栈,即 _request_ctx_stack._local.stack = [ctx]。请求上下文入栈
_request_ctx_stack.push(self) # Open the session at the moment that the request context is available.
# This allows a custom open_session method to use the request context.
# Only open a new session if this is the first time the request was
# pushed, otherwise stream_with_context loses the session.
# 由于每次请求都会初始化创建你ctx,因此session都为None
if self.session is None:
# SecureCookieSessionInterface()
# session_interface = SecureCookieSessionInterface(),即session_interface就是一个SecureCookieSessionInterface类的实例对象
session_interface = self.app.session_interface
# 第一次访问:生成一个 字典(容器) 返回至 self.session
self.session = session_interface.open_session(
self.app, self.request
)
if self.session is None:
self.session = session_interface.make_null_session(self.app)
# 创建 SecureCookieSessionInterface类的实例对象
session_interface = SecureCookieSessionInterface()
def open_session(self, app, request):
# 获取 app的 secret_key
s = self.get_signing_serializer(app)
if s is None:
return None
# 从 cookie 中获取 session 的value
val = request.cookies.get(app.session_cookie_name)
if not val:
# 生成一个 SecureCookieSession类的实例对象,用于保存用户的session数据。 SecureCookieSession类其实就是一个特殊的字典
return self.session_class()
# 第二次访问
max_age = total_seconds(app.permanent_session_lifetime)
try:
# 将 获取到的cookie进行反序列化
data = s.loads(val, max_age=max_age)
# 返回一个含有 cookie信息的 SecureCookieSession 类实例
return self.session_class(data)
except BadSignature:
return self.session_class()
session_class = SecureCookieSession
2.4 在 response = self.full_dispatch_request() 中
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
"""
# 将 _got_first_request = True,依次执行定义的 before_first_request 函数
self.try_trigger_before_first_request_functions()
try:
# 触发 request_started 信号
request_started.send(self)
# 执行钩子函数:before_request,before_first_request
rv = self.preprocess_request()
# 如果执行的before_request,before_first_request函数没有返回值,则继续执行视图函数。若有返回值,则不执行视图函数
if rv is None:
# 执行此url对应的别名的视图函数并执行该函数,返回视图函数的返回值,得到相应信息
rv = self.dispatch_request()
except Exception as e:
# 如果发生错误,则将异常信息作为返回值进行返回
rv = self.handle_user_exception(e)
# 封装返回信息并返回,包括 session
return self.finalize_request(rv)
def finalize_request(self, rv, from_error_handler=False):
"""Given the return value from a view function this finalizes
the request by converting it into a response and invoking the
postprocessing functions. This is invoked for both normal
request dispatching as well as error handlers. Because this means that it might be called as a result of a
failure a special safe mode is available which can be enabled
with the `from_error_handler` flag. If enabled, failures in
response processing will be logged and otherwise ignored. :internal:
"""
response = self.make_response(rv)
try:
response = self.process_response(response)
# 触发 request_finished 信号
request_finished.send(self, response=response)
except Exception:
if not from_error_handler:
raise
self.logger.exception('Request finalizing failed with an '
'error while handling an error')
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):
# 将 self。session 这个字典序列化,并返回,写入到客户端浏览器的cookie中
self.session_interface.save_session(self, ctx.session, response)
return response
3. 在视图函数中对app.session赋值
3.1 session["user"] = 1213
3.2 向 app.session(实际上是向SecureCookieSession类实例中)写值
# 获取 ctx.session
session = LocalProxy(partial(_lookup_req_object, 'session'))
# 偏函数
def _lookup_req_object(name):
# top == ctx
top = _request_ctx_stack.top
if top is None:
raise RuntimeError(_request_ctx_err_msg)
# 返回 ctx.request 或者 ctx.session
return getattr(top, name)
def __init__(self, local, name=None):
# local 指的是 偏函数的内存地址
object.__setattr__(self, '_LocalProxy__local', local)
object.__setattr__(self, '__name__', name)
if callable(local) and not hasattr(local, '__release_local__'):
# "local" is a callable that is not an instance of Local or
# LocalManager: mark it as a wrapped function.
object.__setattr__(self, '__wrapped__', local)
def __setitem__(self, key, value):
# 1.obj = self._get_current_object()
# 2. obj[key] = value
self._get_current_object()[key] = value
def _get_current_object(self):
"""Return the current object. This is useful if you want the real
object behind the proxy at a time for performance reasons or because
you want to pass the object into a different context.
"""
if not hasattr(self.__local, '__release_local__'):
# 执行偏函数,返回session 或者 request
return self.__local()
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError('no object bound to %s' % self.__name__)
对于session的写值操作实际上还是调用的dict类的__setitem__方法
注意:
在flask默认的session中,没有生成随机字符串和进行session数据持久化的操作,而只是单穿把用户赋值给session的数据加密再反序列化之后返回给浏览器。在进行判断的时候也只是单纯的获取cookie中有没有"session"对应的value,可以说在很大程度上造成了数据的不安全。
原生session组件的扩展---flaks_session组件
from flask import Flask,session app = Flask(__name__)
app.secret_key = 'suijksdfsd' # 方式一:
from redis import Redis
from flask_session import RedisSessionInterface
conn = Redis()
app.session_interface = RedisSessionInterface(conn,key_prefix='__',use_signer=False,permanent=True) # 方式二:
# from redis import Redis
# from flask.ext.session import Session
# app.config['SESSION_TYPE'] = 'redis'
# app.config['SESSION_REDIS'] = Redis(host='192.168.0.94',port='6379')
# Session(app) @app.route('/')
def index():
session['xxx'] = 123
return 'Index' if __name__ == '__main__': app.run()
flask源码解析之session的更多相关文章
- flask源码解析之上下文为什么用栈
楔子 我在之前的文章<flask源码解析之上下文>中对flask上下文流程进行了详细的说明,但是在学习的过程中我一直在思考flask上下文中为什么要使用栈完成对请求上下文和应用上下文的入栈 ...
- Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Job Manager 启动
Job Manager 启动 https://t.zsxq.com/AurR3rN 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac ...
- Flink 源码解析 —— Standalone session 模式启动流程
Standalone session 模式启动流程 https://t.zsxq.com/EemAEIi 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0 ...
- Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Task Manager 启动
Task Manager 启动 https://t.zsxq.com/qjEUFau 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Ma ...
- flask源码解析之上下文
引入 对于flask而言,其请求过程与django有着截然不同的流程.在django中是将请求一步步封装最终传入视图函数的参数中,但是在flask中,视图函数中并没有请求参数,而是将请求通过上下文机制 ...
- Flask源码解析:Flask应用执行流程及原理
WSGI WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述服务器端如何与web应用程序通信的 ...
- flask 源码解析:上下文(一)
文章出处 https://www.cnblogs.com/jackchengcc/archive/2018/11/29/10025949.html 一:什么是上下文 每一段程序都有很多外部变量.只有 ...
- flask源码解析之DispatcherMiddleware
DispatcherMiddleware作用 实现多app的应用,完成路由分发的功能 如何使用 from werkzeug.wsgi import DispatcherMiddleware from ...
- Flask源码解析:Flask上下文
一.上下文(Context) 什么是上下文: 每一段程序都有很多外部变量.只有像Add这种简单的函数才是没有外部变量的.一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行.你为了使他们运行, ...
随机推荐
- cmake 头文件 库文件 链接库
原文地址:http://www.cnblogs.com/binbinjx/p/5626916.html 1. 添加头文件目录INCLUDE_DIRECTORIES 语法: include_direct ...
- OAuth 2.0 学习
OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版. 本文对OAuth 2.0的设计思路和运行流程,做一个简明通俗的解释,主要参考材料为R ...
- window.load 和$(document).ready() 、window.load和body onload区别
1.执行时间 window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行. $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕.2.编写个数不同 w ...
- Jigloo 下载 安装 GUI
这个需要授权,一直不能解决!! 网上找了很多,都觉不能访问,这个可以用Eclipse直接更新的 http://www.cloudgardensoftware.com/jigloo/update-sit ...
- HDFS系列 -- HDFS预研
1 HDFS概述 由于传统集中式的物理服务器在存储容量和数据传输速度等方面都有限制,故而越来越不符合这些数据的实际存储需要. 在大数据时代,大数据处理需要解决的首要问题是:如何高效地存储所产生的规模庞 ...
- 2018.09.29 bzoj3166: [Heoi2013]Alo(01trie+双向链表)
传送门 01trie经典题目. 我们可以通过计算每个数作为次小值时对答案的贡献. 显然对于每个iii需要求出一个包含a[i]a[i]a[i]且的区间[l,r][l,r][l,r]且区间所有值都小于a[ ...
- 2018.08.27 rollcall(非旋treap)
描述 初始有一个空集,依次插入N个数Ai.有M次询问Bj,表示询问第Bj个数加入集合后的排名为j的数是多少 输入 第一行是两个整数N,M 接下来一行有N个整数,Ai 接下来一行有M个整数Bj,保证数据 ...
- HDU 1503 Advanced Fruits (LCS+DP+递归)
题意:给定两个字符串,让你求一个最短的字符串,并且这个字符串包含给定的两个. 析:看到这个题,我知道是DP,但是,不会啊...完全没有思路么,我就是个DP渣渣,一直不会做DP. 最后还是参考了一下题解 ...
- x+y+z=n的正整数解
题:x+y+z=n,其中(n>=3),求x,y,z的正整数解的个数根据图象法:x>=1,y>=1,x+y<=n-1
- Restful风格wcf调用4——权限认证
写在前面 在前面的三篇文章,已经介绍了restful风格wcf,如何实现增删改查以及文件的上传下载操作.本篇文章将介绍一下,调用restful的权限认证的内容.在调用的接口,为了安全,总会需要对请求进 ...