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这种简单的函数才是没有外部变量的.一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行.你为了使他们运行, ...
随机推荐
- python 调试方法
一.使用pdb http://blog.csdn.net/wyb_009/article/details/8896744 二.使用gdb 需首先配置gdb pythin支持,步骤如下: 1.修改Pyt ...
- python协程函数、递归、匿名函数与内置函数使用、模块与包
目录: 协程函数(yield生成器用法二) 面向过程编程 递归 匿名函数与内置函数的使用 模块 包 常用标准模块之re(正则表达式) 一.协程函数(yield生成器用法二) 1.生成器的语句形式 a. ...
- 20155333 2016-2017-2 《Java程序设计》第五周学习总结
20155333 2016-2017-2 <Java程序设计>第五周学习总结 教材学习内容总结 1.使用try.catch语法 与C语言中程序流程和错误处理混在一起不同,Java中把正常流 ...
- XE7 里面添加自定义View
经过xe4,xe5,xe6 这么几个版本的磨合,易博龙终于在今年9月推出了统一的多平台开发版本-XE7. 经过最近几天的测试,非常不错.如果各位同学在做移动开发,强烈建议使用XE7. 前面几个版本可以 ...
- 2018.06.30 BZOJ1857: [Scoi2010]传送带(三分套三分)
1857: [Scoi2010]传送带 Time Limit: 1 Sec Memory Limit: 64 MB Description 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段 ...
- HDU 1716 排列2 (格式问题+排列)
题意:. 析:我们完全可以STL里面的函数next_permutation(),然后方便,又简单,这个题坑就是在格式上. 行末不能有空格,结尾不能有空行,不大好控制,必须控制好第一次数. 这个题本应该 ...
- html5获取经纬度
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- hdu2571 命运 2016-09-11 16:54 53人阅读 评论(0) 收藏
命运 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...
- 图片捕获工具driftnet
driftnet是一款简单而使用的图片捕获工具,可以很方便的在网络数据包中抓取图片.该工具可以实时和离线捕获指定数据包中是图片,当然在kali里是有的. 在我之前的一篇博文<kali下搭建WiF ...
- adb命令行输出logcat日志
输出到终端: adb logcat 输出到指定文件:如log.txt adb logcat > log.txt 输出到指定文件并带上日志产生时的系统时间(个人感觉实用的): adb logcat ...