赞
踩
由于http协议是一个无状态的协议,但网站基本上有登录使用的功能,这要求有状态管理,而session机制实现的就是这个功能
session基于cookie实现, 保存在服务端的键值对(形式:{随机字符串:'xxxxxx'}
), 同时在浏览器中的cookie中也对应一相同的随机字符串,用来再次请求的时候验证
用户第一次请求后,将产生的状态信息保存在session中,可以把session当做一个容器,保存了正在使用的所有用户的状态信息,这段状态信息分配一个唯一的标识符用来标识用户的身份,将其保存在响应对象的cookie中
当第二次请求时,解析cookie中的标识符,拿到标识符后去session找到对应的用户的信息
flask的session是通过加密之后放到cookie中,有加密就有密钥用于解密,所以只要用到了flask的session模块就一定要配置"SECRET_KEY"这个全局宏
一般设置为24位的字符,配置方法一般有两种
config.py
SECRET_KEY = 'XXXXXXXXX'
main.py
#encoding: utf-8
from flask import Flask,session
import config
app = Flask(__name__)
直接在主运行文件里面配置。配置config的时候也是和操作字典是一样的
main.py
encoding: utf-8
from flask import Flask,session
app = Flask(__name__)
app.config['SECRET_KEY'] = 'XXXXX' 或者随机数(os.urandom(24))
# 或者
app.secret_key = 'why would I tell you my secret key?'
# key值可以使用随机数,或者自定义
from flask import Flask,session
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
# 设置session
@app.route('/')
def set():
session['username'] = 'liefyuan' # 设置“字典”键值对(正式开发时候,值需要session.get('user')获取)
return 'success'
if __name__ == '__main__':
app.run()
因为session就像字典一样所以,操作它的时候有两种方法:
result = session['key']
:如果内容不存在,将会报异常result = session.get('key')
:如果内容不存在,将返回None(推荐)from flask import Flask,session
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
# 读取session
@app.route('/get')
def get():
# session['username']
# session.get('username')
return session.get('username')
if __name__ == '__main__':
app.run()
#encoding: utf-8
from flask import Flask,session
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
# 删除session
@app.route('/delete/')
def delete():
print session.get('username')
session.pop('username',None) 或者 session['username'] = False
print session.get('username')
return 'success'
if __name__ == '__main__':
app.run()
#encoding: utf-8
from flask import Flask,session
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
# 清除session中所有数据
@app.route('/clear')
def clear():
print session.get('username')
# 清除session中所有数据
session.clear
print session.get('username')
return 'success'
if __name__ == '__main__':
app.run()
依据上述session的原理,分析一下flask框架的session机制实现的过程
Flask对象使用open_session方法和save_session方法打开和保存会话信息,请求在创建请求上下文后会调用open_session方法获取用户的信息,在执行完处理逻辑后会调用save_session方法保存用户的信息
open_session和save_session
def open_session(self, request):
# 调用了app的session_interface对象的方法
return self.session_interface.open_session(self, request)
def save_session(self, session, response):
return self.session_interface.save_session(self, session, response)
app对象默认的session_interface = SecureCookieSessionInterface()
,SecureCookieSessionInterface
重写SessionInterface
对象的open_session
方法和save_session
方法
class SecureCookieSessionInterface(SessionInterface):
pass
def open_session(self, app, request):
# 检测是否设置了secret_key参数,返回一个签名对象
s = self.get_signing_serializer(app)
if s is None:
return None
# 去cookie中获取session信息
val = request.cookies.get(app.session_cookie_name)
# 如果是第一次请求,返回一个空的SecureCookieSession对象,会被交给请求上下文的session属性管理
if not val:
return self.session_class()
# 获取session的失效时间
max_age = total_seconds(app.permanent_session_lifetime)
try:
# 对session信息进行解码得到用户信息
data = s.loads(val, max_age=max_age)
# 返回有用户信息的session对象
return self.session_class(data)
except BadSignature:
return self.session_class()
def save_session(self, app, session, response):
# 获取cookie设置的域
domain = self.get_cookie_domain(app)
# 获取cookie设置的路径
path = self.get_cookie_path(app)
...
# 检测SESSION_REFRESH_EACH_REQUEST参数配置
if not self.should_set_cookie(app, session):
return
# 返回SESSION_COOKIE_HTTPONLY参数配置
httponly = self.get_cookie_httponly(app)
# 返回SESSION_COOKIE_SECURE参数配置
secure = self.get_cookie_secure(app)
# 返回失效的时间点
expires = self.get_expiration_time(app, session)
#将用户的数据加密
val = self.get_signing_serializer(app).dumps(dict(session))
# 设置cookie
response.set_cookie(app.session_cookie_name, val,
expires=expires, httponly=httponly,
domain=domain, path=path, secure=secure)
请求上下文RequestContext的session属性是一个SecureCookieSession对象,可以将其看做一个字典
通过分析flask的session实现机制,一般认为将session信息放在cookie中不够保险,那么可以实现session机制,思路是创建一个类继承SessionInterface,然后重写open_session方法和save_session方法,再替换app的session_interface属性即可
# 配置session存放的路径
MY_SESSION_PATH = '\session.json'
'SESSION_TYPE' = 'file'
# 配置默认的seesion的配置参数
SECRET_KEY = '123'
SESSION_USE_SIGNER = True
# session的有效期,单位:秒
PERMANENT_SESSION_LIFETIME = 7200
from flask.sessions import *
try:
import cPickle as pickle
except ImportError:
import pickle
import json
from uuid import uuid4
import time
# 我们需要自定义一个Session对象用来存储用户的信息,它使用一个唯一的id标识,模仿SecureCookieSession的实现方法
class SecureFileSession(CallbackDict, SessionMixin):
def __init__(self, initial=None, sid=None, permanent=None):
def on_update(self):
self.modified = True
CallbackDict.__init__(self, initial, on_update)
self.sid = sid # session的标识
if permanent:
self.permanent = permanent # 失效时间
self.modified = False
# 我们使用uuid作为签名,省略校验过程
class NewSessionInterface(SessionInterface):
def _generate_sid(self):
return str(uuid4())
class JsonFileSessionInterface(NewSessionInterface):
# 用来序列化的包
serializer = pickle
session_class = SecureFileSession
def __init__(self, app=None):
self.app = app
if app is not None:
self.init_app(app)
def init_app(self, app):
"""
替换app的session_interface属性
:param app:
:return:
"""
app.session_interface = self._get_interface(app)
def _get_interface(self, app):
"""
加载配置参数返回本身,必须配置'SESSION_TYPE'和'MY_SESSION_PATH'参数,否则使用默认的session
:param app:
:return:
"""
config = app.config.copy()
if config['SESSION_TYPE'] == 'file':
if not config['MY_SESSION_PATH']:
return SecureCookieSessionInterface()
self.path = app.static_folder + config['MY_SESSION_PATH'] # session文件路径
self.permanent = total_seconds(app.permanent_session_lifetime) # 失效时间
return self
return SecureCookieSessionInterface()
def open_session(self, app, request):
"""
从文件中获取session数据
:param app:
:param request:
:return:
"""
# 获取session签名
sid = request.cookies.get(app.session_cookie_name)
permanent = int(time.time()) + self.permanent
# 如果没有说明是第一次访问,返回空session对象
if not sid:
# 获取一个uuid
sid = self._generate_sid()
return self.session_class(sid=sid, permanent=permanent)
with open(self.path, 'r', encoding='utf-8') as f:
v = f.read()
# 如果session为空,返回空session对象
if not v:
return self.session_class(sid=sid, permanent=permanent)
try:
val = json.loads(v)
except ValueError as e:
print('配置参数错误:{}'.format(e))
return self.session_class(sid=sid, permanent=permanent)
else:
self.val = val
# 通过sid获取信息
data = val.get(sid)
if not data:
return self.session_class(sid=sid, permanent=permanent)
# 判断以前的信息是否超时
if permanent - int(data['permanent']) > self.permanent:
return self.session_class(sid=sid, permanent=permanent)
return self.session_class(data, sid=sid)
def save_session(self, app, session, response):
"""
保存session信息
:param app:
:param session:
:param response:
:return:
"""
# 前面借鉴flask默认的实现方式
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
if not session:
if session.modified:
response.delete_cookie(app.session_cookie_name,
domain=domain, path=path)
return
if not self.should_set_cookie(app, session):
return
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
expires = self.get_expiration_time(app, session)
# 将session信息保存在文件中
session.update({'permanent': int(time.time()) + self.permanent})
if hasattr(self, 'val') and isinstance(self.val, dict):
self.val.update({session.sid: dict(session)})
else:
self.val = {session.sid: dict(session)}
with open(self.path, 'w', encoding='utf-8') as f:
result = json.dumps(self.val)
f.write(result)
response.set_cookie(app.session_cookie_name, session.sid,
expires=expires, httponly=httponly,
domain=domain, path=path, secure=secure)
app = Flask(__name__,template_folder='static/html')
app.config.update({
'SECRET_KEY':'123',
'SESSION_USE_SIGNER':True,
'SESSION_TYPE':'file',
'MY_SESSION_PATH':'\session.json'})
from session_file import JsonFileSessionInterface
se = JsonFileSessionInterface(app=app)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=80, debug=True)
经过上面的三步,可以将自己实现的session对象运用到flask项目中,采用的是文件存储session,实际项目中有redis、memcached、mysql等都可以存储session,将它们整合起来,于是flask_session插件就应运而生
flask_session插件就是官方推荐的session实现插件,整合了redis、memcached、mysql、file、mongodb等多种第三方存储session信息,实现原理就是上面自定义session所做的工作
pip install Flask-Session
flask_session初始化后,从app的配置中读取参数,比较重要的有:
设置session保存的位置,可以有多种配置,
SESSION_TYPE = ‘null’ : 采用flask默认的保存在cookie中;
SESSION_TYPE = ‘redis’ : 保存在redis中
SESSION_TYPE = ‘memcached’ : 保存在memcache
SESSION_TYPE = 'filesystem' : 保存在文件
SESSION_TYPE = 'mongodb' : 保存在MongoDB
SESSION_TYPE = 'sqlalchemy' : 保存在关系型数据库
SESSION_KEY_PREFIX = 'session:' :session存储时的键的前缀
SESSION_USE_SIGNER:是否为cookie设置签名来保护数据不被更改,默认是False;如果设置True,那么必须设置flask的secret_key参数;
SESSION_PERMANENT:是否使用永久会话,默认True,但是如果设置了PERMANENT_SESSION_LIFETIME,则这个失效;
SESSION_REDIS:
如果SESSION_TYPE = ‘redis’,那么设置该参数连接哪个redis,其是一个连接对象;如果不设置的话,默认连接127.0.0.1:6379/0
for example:
SESSION_REDIS = redis.StrictRedis(host="127.0.0.1", port=6390, db=4)
# 指明对session数据进行保护
SECRET_KEY = '123'
SESSION_USE_SIGNER = True
# 指明保存到redis中
SESSION_TYPE = "redis"
SESSION_REDIS = redis.StrictRedis(host="127.0.0.1", port=6390, db=4)
# session的有效期,单位:秒
PERMANENT_SESSION_LIFETIME = 7200
# extensions.py
# 创建一个session对象
from flask_session import Session
# 创建一个Session的实例
session = Session()
# 在app初始化时初始化session对象,即加载配置
# __init__.py
from flask import Flask
app = Flask(__name__)
session.init_app(app=app)
# task.py
from Flask import session
@app.route('/test', methods=['POST'])
def test():
session.get('user',None)
return ""
#encoding: utf-8
from flask import Flask,session
from datetime import timedelta
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) # 配置7天有效
# 设置session
@app.route('/')
def set():
session['username'] = 'liefyuan'
session.permanent = True
return 'success'
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。