前言

flask_session是flask框架实现session功能的一个插件,用来替代flask自带的session实现机制,flask默认的session信息保存在cookie中,不够安全和灵活。

flask的session机制

session是用来干什么的呢?由于http协议是一个无状态的协议,也就是说同一个用户第一次请求和第二次请求是完全没有关系的,但是现在的网站基本上有登录使用的功能,这就要求必须实现有状态,而session机制实现的就是这个功能。

实现的原理:

用户第一次请求后,将产生的状态信息保存在session中,这时可以把session当做一个容器,它保存了正在使用的所有用户的状态信息;这段状态信息分配了一个唯一的标识符用来标识用户的身份,将其保存在响应对象的cookie中;当第二次请求时,解析cookie中的标识符,拿到标识符后去session找到对应的用户的信息。

简单使用

  1. from flask import Flask,session
  2. app = Flask(__name__)
  3. @app.route('/test1/')
  4. def test():
  5. session.setdefault('name', 'xiaoming')
  6. return 'OK'
  7. if __name__ == '__main__':
  8. app.run(host='127.0.0.1', port=80, debug=True)

在flask中,如果我们想要获取session信息,直接通过flask的session获取就可以了,这是因为session是一个代理对象,代理当前请求上下文的session属性。

session源码分析

依据上述session的原理,来分析一下flask框架的session机制实现的过程。

Flask对象使用open_session方法和save_session方法打开和保存会话信息,请求在创建请求上下文后会调用open_session方法获取用户的信息,在执行完处理逻辑后会调用save_session方法保存用户的信息。

  • open_session和save_session
  1. def open_session(self, request):
  2. # 调用了app的session_interface对象的方法
  3. return self.session_interface.open_session(self, request)
  4. def save_session(self, session, response):
  5. return self.session_interface.save_session(self, session, response)

app对象默认的session_interface = SecureCookieSessionInterface(),SecureCookieSessionInterface重写了SessionInterface对象的open_session方法和save_session方法。

  1. class SecureCookieSessionInterface(SessionInterface):
  2. pass
  3. def open_session(self, app, request):
  4. # 检测是否设置了secret_key参数,返回一个签名对象
  5. s = self.get_signing_serializer(app)
  6. if s is None:
  7. return None
  8. # 去cookie中获取session信息
  9. val = request.cookies.get(app.session_cookie_name)
  10. # 如果是第一次请求,返回一个空的SecureCookieSession对象,会被交给请求上下文的session属性管理
  11. if not val:
  12. return self.session_class()
  13. # 获取session的失效时间
  14. max_age = total_seconds(app.permanent_session_lifetime)
  15. try:
  16. # 对session信息进行解码得到用户信息
  17. data = s.loads(val, max_age=max_age)
  18. # 返回有用户信息的session对象
  19. return self.session_class(data)
  20. except BadSignature:
  21. return self.session_class()
  22. def save_session(self, app, session, response):
  23. # 获取cookie设置的域
  24. domain = self.get_cookie_domain(app)
  25. # 获取cookie设置的路径
  26. path = self.get_cookie_path(app)
  27. ...
  28. # 检测SESSION_REFRESH_EACH_REQUEST参数配置
  29. if not self.should_set_cookie(app, session):
  30. return
  31. # 返回SESSION_COOKIE_HTTPONLY参数配置
  32. httponly = self.get_cookie_httponly(app)
  33. # 返回SESSION_COOKIE_SECURE参数配置
  34. secure = self.get_cookie_secure(app)
  35. # 返回失效的时间点
  36. expires = self.get_expiration_time(app, session)
  37. #将用户的数据加密
  38. val = self.get_signing_serializer(app).dumps(dict(session))
  39. # 设置cookie
  40. response.set_cookie(app.session_cookie_name, val,
  41. expires=expires, httponly=httponly,
  42. domain=domain, path=path, secure=secure)

请求上下文RequestContext的session属性是一个SecureCookieSession对象,可以将其看做一个字典;

相关的配置参数

  1. SESSION_COOKIE_NAME:设置返回给客户端的cookie的名称,默认是“session”;放置在response的头部;
  2. SESSION_COOKIE_DOMAIN:设置会话的域,默认是当前的服务器,因为Session是一个全局的变量,可能应用在多个app中;
  3. SESSION_COOKIE_PATH:设置会话的路径,即哪些路由下应该设置cookie,如果不设置,那么默认为‘/’,所有的路由都会设置cookie;这个参数和SESSION_COOKIE_DOMAIN是互斥的
  4. SERVER_NAME:设置服务器的名字,一般不用;
  5. SESSION_COOKIE_SECURE:如果 cookie 标记为“ secure ”,那么浏览器只会使用基于 HTTPS 的请求发 cookie,应用必须使用 HTTPS 服务来启用本变量,默认False
  6. APPLICATION_ROOT:设置应用的根路径;
  7. SESSION_REFRESH_EACH_REQUEST:是否应该为每一个请求设置cookie,默认为True,如果为False则必须显性调用set_cookie函数;
  8. SESSION_COOKIE_HTTPONLYcookie应该和httponly标志一起设置,默认为True,这个一般采用默认。
  9. PERMANENT_SESSION_LIFETIME:设置session的有效期,即cookie的失效时间,单位是s。这个参数很重要,因为默认会话是永久性的。
  10. SESSION_COOKIE_HTTPONLY:默认为true,表示允许js脚本访问cookie

小结

flask默认通过SecureCookieSessionInterface对象管理session,其重写了SessionInterface对象的open_session方法和save_session方法,将用户的数据加密后存储在cookie中。

自定义session存储

通过分析flask的session实现机制,一般认为将session信息放在cookie中不够保险,那么我们可以实现自己的session机制,思路是创建一个类继承SessionInterface,然后重写open_session方法和save_session方法,再使用我们的类替换app的session_interface属性即可。

比如我要将session信息保存在一个session.json中。

第一步:设置必要的配置参数
  1. # 配置session存放的路径
  2. MY_SESSION_PATH = '\session.json'
  3. 'SESSION_TYPE' = 'file'
  4. # 配置默认的seesion的配置参数
  5. SECRET_KEY = '123'
  6. SESSION_USE_SIGNER = True
  7. # session的有效期,单位:秒
  8. PERMANENT_SESSION_LIFETIME = 7200
第二步:创建自己的SessionInterface的子类
  1. from flask.sessions import *
  2. try:
  3. import cPickle as pickle
  4. except ImportError:
  5. import pickle
  6. import json
  7. from uuid import uuid4
  8. import time
  9. # 我们需要自定义一个Session对象用来存储用户的信息,它使用一个唯一的id标识,模仿SecureCookieSession的实现方法
  10. class SecureFileSession(CallbackDict, SessionMixin):
  11. def __init__(self, initial=None, sid=None, permanent=None):
  12. def on_update(self):
  13. self.modified = True
  14. CallbackDict.__init__(self, initial, on_update)
  15. self.sid = sid # session的标识
  16. if permanent:
  17. self.permanent = permanent # 失效时间
  18. self.modified = False
  19. # 我们使用uuid作为签名,省略校验过程
  20. class NewSessionInterface(SessionInterface):
  21. def _generate_sid(self):
  22. return str(uuid4())
  23. class JsonFileSessionInterface(NewSessionInterface):
  24. # 用来序列化的包
  25. serializer = pickle
  26. session_class = SecureFileSession
  27. def __init__(self, app=None):
  28. self.app = app
  29. if app is not None:
  30. self.init_app(app)
  31. def init_app(self, app):
  32. """
  33. 替换app的session_interface属性
  34. :param app:
  35. :return:
  36. """
  37. app.session_interface = self._get_interface(app)
  38. def _get_interface(self, app):
  39. """
  40. 加载配置参数返回本身,必须配置'SESSION_TYPE'和'MY_SESSION_PATH'参数,否则使用默认的session
  41. :param app:
  42. :return:
  43. """
  44. config = app.config.copy()
  45. if config['SESSION_TYPE'] == 'file':
  46. if not config['MY_SESSION_PATH']:
  47. return SecureCookieSessionInterface()
  48. self.path = app.static_folder + config['MY_SESSION_PATH'] # session文件路径
  49. self.permanent = total_seconds(app.permanent_session_lifetime) # 失效时间
  50. return self
  51. return SecureCookieSessionInterface()
  52. def open_session(self, app, request):
  53. """
  54. 从文件中获取session数据
  55. :param app:
  56. :param request:
  57. :return:
  58. """
  59. # 获取session签名
  60. sid = request.cookies.get(app.session_cookie_name)
  61. permanent = int(time.time()) + self.permanent
  62. # 如果没有说明是第一次访问,返回空session对象
  63. if not sid:
  64. # 获取一个uuid
  65. sid = self._generate_sid()
  66. return self.session_class(sid=sid, permanent=permanent)
  67. with open(self.path, 'r', encoding='utf-8') as f:
  68. v = f.read()
  69. # 如果session为空,返回空session对象
  70. if not v:
  71. return self.session_class(sid=sid, permanent=permanent)
  72. try:
  73. val = json.loads(v)
  74. except ValueError as e:
  75. print('配置参数错误:{}'.format(e))
  76. return self.session_class(sid=sid, permanent=permanent)
  77. else:
  78. self.val = val
  79. # 通过sid获取信息
  80. data = val.get(sid)
  81. if not data:
  82. return self.session_class(sid=sid, permanent=permanent)
  83. # 判断以前的信息是否超时
  84. if permanent - int(data['permanent']) > self.permanent:
  85. return self.session_class(sid=sid, permanent=permanent)
  86. return self.session_class(data, sid=sid)
  87. def save_session(self, app, session, response):
  88. """
  89. 保存session信息
  90. :param app:
  91. :param session:
  92. :param response:
  93. :return:
  94. """
  95. # 前面借鉴flask默认的实现方式
  96. domain = self.get_cookie_domain(app)
  97. path = self.get_cookie_path(app)
  98. if not session:
  99. if session.modified:
  100. response.delete_cookie(app.session_cookie_name,
  101. domain=domain, path=path)
  102. return
  103. if not self.should_set_cookie(app, session):
  104. return
  105. httponly = self.get_cookie_httponly(app)
  106. secure = self.get_cookie_secure(app)
  107. expires = self.get_expiration_time(app, session)
  108. # 将session信息保存在文件中
  109. session.update({'permanent': int(time.time()) + self.permanent})
  110. if hasattr(self, 'val') and isinstance(self.val, dict):
  111. self.val.update({session.sid: dict(session)})
  112. else:
  113. self.val = {session.sid: dict(session)}
  114. with open(self.path, 'w', encoding='utf-8') as f:
  115. result = json.dumps(self.val)
  116. f.write(result)
  117. response.set_cookie(app.session_cookie_name, session.sid,
  118. expires=expires, httponly=httponly,
  119. domain=domain, path=path, secure=secure)
第三步:初始化替换app的session_interface
  1. app = Flask(__name__,template_folder='static/html')
  2. app.config.update({
  3. 'SECRET_KEY':'123',
  4. 'SESSION_USE_SIGNER':True,
  5. 'SESSION_TYPE':'file',
  6. 'MY_SESSION_PATH':'\session.json'})
  7. from session_file import JsonFileSessionInterface
  8. se = JsonFileSessionInterface(app=app)
  9. if __name__ == '__main__':
  10. app.run(host='127.0.0.1', port=80, debug=True)

小结

经过上面的三步,我们就可以将自己实现的session对象运用到flask项目中,我们采用的是文件存储session,实际项目中有redis,memcached,mysql等都可以存储session,将它们整合起来,于是flask_session插件就应运而生了。

flask_session扩展

flask_session插件就是官方推荐的session实现插件,整合了redis,memcached,mysql,file,mongodb等多种第三方存储session信息,它的实现原理就是我上面自定义session所做的工作。

安装

  1. pip install Flask-Session

配置参数详解

flask_session初始化后,会从app的配置中读取参数,比较重要的有:

  1. 设置session保存的位置,可以有多种配置,
  2. SESSION_TYPE = null : 采用flask默认的保存在cookie中;
  3. SESSION_TYPE = redis : 保存在redis
  4. SESSION_TYPE = memcached : 保存在memcache
  5. SESSION_TYPE = 'filesystem' : 保存在文件
  6. SESSION_TYPE = 'mongodb' : 保存在MongoDB
  7. SESSION_TYPE = 'sqlalchemy' : 保存在关系型数据库
  8. SESSION_KEY_PREFIX = 'session:' :session存储时的键的前缀
  9. SESSION_USE_SIGNER:是否为cookie设置签名来保护数据不被更改,默认是False;如果设置True,那么必须设置flasksecret_key参数;
  10. SESSION_PERMANENT:是否使用永久会话,默认True,但是如果设置了PERMANENT_SESSION_LIFETIME,则这个失效;
  11. SESSION_REDIS
  12. 如果SESSION_TYPE = redis’,那么设置该参数连接哪个redis,其是一个连接对象;如果不设置的话,默认连接127.0.0.1:6379/0
  13. for example:
  14. SESSION_REDIS = redis.StrictRedis(host="127.0.0.1", port=6390, db=4)

关于其他的保存中间人参考:https://pythonhosted.org/Flask-Session/

一份常用的flask_session的配置

  1. # 指明对session数据进行保护
  2. SECRET_KEY = '123'
  3. SESSION_USE_SIGNER = True
  4. # 指明保存到redis中
  5. SESSION_TYPE = "redis"
  6. SESSION_REDIS = redis.StrictRedis(host="127.0.0.1", port=6390, db=4)
  7. # session的有效期,单位:秒
  8. PERMANENT_SESSION_LIFETIME = 7200

flask_session的使用流程

  1. # extensions.py
  2. # 创建一个session对象
  3. from flask_session import Session
  4. # 创建一个Session的实例
  5. session = Session()
  6. # 在app初始化时初始化session对象,即加载配置
  7. # __init__.py
  8. from flask import Flask
  9. app = Flask(__name__)
  10. session.init_app(app=app)
  11. # task.py
  12. from Flask import session
  13. @app.route('/test', methods=['POST'])
  14. def test():
  15. session.get('user',None)
  16. return ""

参考:

flask基础之session原理详解(十)的更多相关文章

  1. 【Java基础】HashMap原理详解

    哈希表(hash table) 也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,本文会对java集合框架中Has ...

  2. javaweb基础(7)_HttpServletResponse原理详解

    Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象.request和response对象即然代表请求和响应,那我们要 ...

  3. I2C 基础原理详解

    今天来学习下I2C通信~ I2C(Inter-Intergrated Circuit)指的是 IC(Intergrated Circuit)之间的(Inter) 通信方式.如上图所以有很多的周边设备都 ...

  4. SSL/TLS 原理详解

    本文大部分整理自网络,相关文章请见文后参考. SSL/TLS作为一种互联网安全加密技术,原理较为复杂,枯燥而无味,我也是试图理解之后重新整理,尽量做到层次清晰.正文开始. 1. SSL/TLS概览 1 ...

  5. 【转】Cookie/Session机制详解

    Cookie/Session机制详解   会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用的会话跟踪技术是Cookie与Session.Cookie通过在客户端记录信息 ...

  6. [No0000126]SSL/TLS原理详解与WCF中的WS-Security

    SSL/TLS作为一种互联网安全加密技术 1. SSL/TLS概览 1.1 整体结构 SSL是一个介于HTTP协议与TCP之间的一个可选层,其位置大致如下: SSL:(Secure Socket La ...

  7. Java基础-面向接口编程-JDBC详解

    Java基础-面向接口编程-JDBC详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.JDBC概念和数据库驱动程序 JDBC(Java Data Base Connectiv ...

  8. Influxdb原理详解

    本文属于<InfluxDB系列教程>文章系列,该系列共包括以下 15 部分: InfluxDB学习之InfluxDB的安装和简介 InfluxDB学习之InfluxDB的基本概念 Infl ...

  9. 【转】VLAN原理详解

    1.为什么需要VLAN 1.1 什么是VLAN? VLAN(Virtual LAN),翻译成中文是“虚拟局域网”.LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成的企业网络.V ...

随机推荐

  1. 学习电脑编码utf-8,ansi编码的基础知识等

    大学时期就很好奇,我们所看到的文字在电脑里面是怎么记忆的,感觉不可能是文字本身,今天刚好学习java的io流知识,顺便补充了一下电脑编码知识,先看一下下面小王和小张的例子,然后思考电脑怎么存放文字?  ...

  2. Ubuntu安装eclipse,并创建桌面快捷方式

    系统:Ubuntu 16.04 JDK版本:1.8.0_121 Ubuntu下安装JDK配置环境变量可见我的这篇文章   http://www.cnblogs.com/AloneZ/p/Ubuntu1 ...

  3. JS开发之CommonJs和AMD/CMD规范

    CommonJS是主要为了JS在后端的表现制定的,他是不适合前端的,AMD(异步模块定义)出现了,它就主要为前端JS的表现制定规范. 在兼容CommonJS的系统中,你可以使用JavaScript开发 ...

  4. 团队项目设计完善&编码测试

    任务1:软件设计方案说明书 <基于弹幕评论的大数据分析平台软件设计方案说明书>仓库链接:点击跳转 任务2:搭建并配置项目集成开发环境: 开发环境 java version "1. ...

  5. 【补】debug

    懒得翻别人博客了,之前的按钮不显示名字,应该是文字ui文件名写错了. 现在不存在任何已知bug.

  6. 【iMooc】全面解析java注解

    在慕课上学习了一个关于java注解的课程,下面是笔记以及一些源码. Annotation——注解 1.JDK中的注解 JDK中包括下面三种注解: @Override:标记注解(marker annot ...

  7. helm的安装于与简单使用

    根据 csdn 博客整理学习 原始博客地址: https://blog.csdn.net/weiguang1017/article/details/78045013 1. 下载所需要的文件: 客户端文 ...

  8. python自动化之鼠标移动

    ################################用GUI自动化控制键盘和鼠标############################### ''' http://pyautogui.r ...

  9. 一本通1642【例 2】Fibonacci 第 n 项

    1642: [例 2]Fibonacci 第 n 项 sol:挺模板的吧,经典题吧qaq (1) 1 0    *     1 1     =   1 1 1 0 (2) 1 1    *     1 ...

  10. ubuntu系统创建新用户并赋予sudo权限

    1.创建新用户 创建新用户有两种方式:adduser和useradd adduser会为用户创建組./home目录下同名文件夹,密码,而useradd不会 因此推荐使用adduser创建用户,例: s ...