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这种简单的函数才是没有外部变量的.一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行.你为了使他们运行, ...
随机推荐
- PAT 1078 字符串压缩与解压(20)(代码+思路)
1078 字符串压缩与解压(20 分) 文本压缩有很多种方法,这里我们只考虑最简单的一种:把由相同字符组成的一个连续的片段用这个字符和片段中含有这个字符的个数来表示.例如 ccccc 就用 5c 来表 ...
- Vue-cli 配置开发环境让测试服务器监听所有IP
//config/inex.js // Various Dev Server settingshost: '0.0.0.0', // can be overwritten by process.env ...
- stl string 使用指定的分隔符分割成数个子字符串
#include <iostream> #include <vector> #include <string> #include <algorithm> ...
- RocketMQ的客户端连接数调查
RocketMQ版本:3.4.6 ==问题现象== RocketMQ集群的某个topic,在一部分节点上消费有“断层”,这部分数据一致没办法消费. ==调查过程== 一顿操作猛如虎的调查之后发现, 该 ...
- javac 编译java文件提示: 程序包com.mysql.jdbc不存在
需要将引用的包放到:/usr/java/jdk1.7.0_75/jre/lib/ext 也就是jdk安装目录/jre/lib/ext 目录下面
- 20155335俞昆《java程序设计》第七周总结
学号 2016-2017-2 <Java程序设计>第X周学习总结 ## 教材学习内容总结 Lambda 的语法概览 String[] names={“Justin”,”cater ...
- 2018.10.22 bzoj1009: [HNOI2008]GT考试(kmp+矩阵快速幂优化dp)
传送门 f[i][j]f[i][j]f[i][j]表示从状态"匹配了前i位"转移到"匹配了前j位"的方案数. 这个东西单次是可以通过跳kmp的fail数组得到的 ...
- Shell 中expr的使用
1.expr命令一般用于整数值,其一般格式为:expr argument operator argument一般的用法是使用expr做算术运算,如:[root@centos ~]# expr 10 + ...
- LA 4670 Dominating Patterns (AC自动机)
题意:给定n个字符串和一个文本串,查找哪个字符串出现的次数的最多. 析:一匹配多,很明显是AC自动机.只需要对原来的进行修改一下,就可以得到这个题的答案, 计算过程中,要更新次数,并且要映射字符串.如 ...
- Git客户端命令总结
一:常用命令 1.先进入项目目录,然后git init:则会为此 项目/目录 创建一个本地仓库(或重新初始化这个本地仓库),可以用ls -a ./看到多了.git目录: 或者git init /hom ...