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这种简单的函数才是没有外部变量的.一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行.你为了使他们运行, ...
随机推荐
- spring boot2.03 spring cloud Finchley.RELEASE遇到的问题
1.spring cloud bus 本地不能更新 原因是@RefreshScope注解要加在需要更新的controller上 2.No instances found of configserver ...
- loadrunner12.5-vugen回放脚本提示:URL=“http://www.testclass.net/js/scripts.js”的常规连接当前无套接字 (16 不足) 可用,是什么意思呢?怎么理解呢?
会发生这个报错,是因为每个浏览器都有一个限制,检查哪个浏览器客户正在模拟, 通常只允许16个并发连接. 如果超过此超过接数,将显示该消息,通知您没有可用的连接. 而max connection的默认值 ...
- 讲真的,千万别得罪会PS的人
传说中有一种软件炒鸡可怕 那就是PS,专业术语是photoshop! 它能让你貌美如花 也能让你瞬间丑得掉渣 更可怕的是网络上大神的出现 简直让我们难以想象的厉害! 下面大家一起来欣赏一下 那些大神帮 ...
- 【附源文件】日记类App原型制作分享-Grid Diary
Grid Diary是一款非常受文艺青年喜爱的记录应用,它设计简单,内容却非常丰富.它不再是单调的文字记录,界面的设计非常与众不同,由许多格子拼凑而成,每一个格子里面还带有一个问题,十分有趣.提到格子 ...
- group by 和 distinct 的区别
SELECT fs.card_id, fs. NAME, fs.email, fs.phone_num, fs.weixin_num, fs.permission, fs.open_id FROM f ...
- 2018.10.09 NOIP模拟 好数(双向搜索)
传送门 直接双向搜索出两边可行解,然后把两边的可行解合并起来得出答案就行了. 注意合并的时候可以利用排序和单调性优化时间复杂度. 直接枚举合并是O(siza∗sizb)O(siza*sizb)O(si ...
- 2018.08.10 atcoder Median Sum(01背包)
传送门 题意简述:输入一个数组an" role="presentation" style="position: relative;">anan. ...
- 2018.07.08 POJ 2481 Cows(线段树)
Cows Time Limit: 3000MS Memory Limit: 65536K Description Farmer John's cows have discovered that the ...
- flask_login
一.配置 对于登录系统,我们将会使用到两个扩展,Flask-Login 和 Flask-OpenID.在microblog.py中配置: Flask-OpenID 扩展需要一个存储文件的临时文件夹的路 ...
- http://blog.csdn.net/zgl07/article/details/43491399
转载申明:本文转载自http://www.brendangregg.com/perf.html 请大家看了之后如果要转载一定要注上这个地址!!! ========================= ...