flask_session是flask框架實現session功能的一個插件,用來替代flask自帶的session實現機制,flask默認的session信息保存在cookie中,不夠安全和靈活。html
session是用來幹什麼的呢?因爲http協議是一個無狀態的協議,也就是說同一個用戶第一次請求和第二次請求是徹底沒有關係的,可是如今的網站基本上有登陸使用的功能,這就要求必須實現有狀態,而session機制實現的就是這個功能。python
實現的原理:mysql
用戶第一次請求後,將產生的狀態信息保存在session中,這時能夠把session當作一個容器,它保存了正在使用的全部用戶的狀態信息;這段狀態信息分配了一個惟一的標識符用來標識用戶的身份,將其保存在響應對象的cookie中;當第二次請求時,解析cookie中的標識符,拿到標識符後去session找到對應的用戶的信息。redis
from flask import Flask,session app = Flask(__name__) @app.route('/test1/') def test(): session.setdefault('name', 'xiaoming') return 'OK' if __name__ == '__main__': app.run(host='127.0.0.1', port=80, debug=True)
在flask中,若是咱們想要獲取session信息,直接經過flask的session獲取就能夠了,這是由於session是一個代理對象,代理當前請求上下文的session屬性。sql
依據上述session的原理,來分析一下flask框架的session機制實現的過程。mongodb
Flask對象使用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方法。json
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_COOKIE_NAME:設置返回給客戶端的cookie的名稱,默認是「session」;放置在response的頭部; SESSION_COOKIE_DOMAIN:設置會話的域,默認是當前的服務器,由於Session是一個全局的變量,可能應用在多個app中; SESSION_COOKIE_PATH:設置會話的路徑,即哪些路由下應該設置cookie,若是不設置,那麼默認爲‘/’,全部的路由都會設置cookie;這個參數和SESSION_COOKIE_DOMAIN是互斥的 SERVER_NAME:設置服務器的名字,通常不用; SESSION_COOKIE_SECURE:若是 cookie 標記爲「 secure 」,那麼瀏覽器只會使用基於 HTTPS 的請求發 送 cookie,應用必須使用 HTTPS 服務來啓用本變量,默認False APPLICATION_ROOT:設置應用的根路徑; SESSION_REFRESH_EACH_REQUEST:是否應該爲每個請求設置cookie,默認爲True,若是爲False則必須顯性調用set_cookie函數; SESSION_COOKIE_HTTPONLY:cookie應該和httponly標誌一塊兒設置,默認爲True,這個通常採用默認。 PERMANENT_SESSION_LIFETIME:設置session的有效期,即cookie的失效時間,單位是s。這個參數很重要,由於默認會話是永久性的。 SESSION_COOKIE_HTTPONLY:默認爲true,表示容許js腳本訪問cookie;
flask默認經過SecureCookieSessionInterface對象管理session,其重寫了SessionInterface對象的open_session方法和save_session方法,將用戶的數據加密後存儲在cookie中。瀏覽器
經過分析flask的session實現機制,通常認爲將session信息放在cookie中不夠保險,那麼咱們能夠實現本身的session機制,思路是建立一個類繼承SessionInterface,而後重寫open_session方法和save_session方法,再使用咱們的類替換app的session_interface屬性便可。
好比我要將session信息保存在一個session.json中。
# 配置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)
關於其餘的保存中間人蔘考:https://pythonhosted.org/Flask-Session/
# 指明對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 ""
https://pythonhosted.org/Flask-Session/
https://dormousehole.readthedocs.io/en/latest