flask session組件的使用示例
一、簡(jiǎn)介
flask中session組件可分為內(nèi)置的session組件還有第三方flask-session組件,內(nèi)置的session組件功能單一,而第三方的flask-sessoin可支持redis、memcached、文件等進(jìn)行session的存儲(chǔ)。以下將介紹內(nèi)置session以及第三方session組件的使用方法以及處理機(jī)制。
二、內(nèi)置session處理機(jī)制
Cookie與Session
Cookie:
Cookie意為“甜餅”,是由W3C組織提出,最早由Netscape社區(qū)發(fā)展的一種機(jī)制。目前Cookie已經(jīng)成為標(biāo)準(zhǔn),所有的主流瀏覽器如IE、Netscape、Firefox、Opera等都支持Cookie。由于HTTP是一種無狀態(tài)的協(xié)議,服務(wù)器單從網(wǎng)絡(luò)連接上無從知道客戶身份。怎么辦呢?就給客戶端們頒發(fā)一個(gè)通行證吧,每人一個(gè),無論誰訪問都必須攜帶自己通行證。這樣服務(wù)器就能從通行證上確認(rèn)客戶身份了。這就是Cookie的工作原理。
Cookie實(shí)際上是一小段的文本信息。客戶端請(qǐng)求服務(wù)器,如果服務(wù)器需要記錄該用戶狀態(tài),就使用response向客戶端瀏覽器頒發(fā)一個(gè)Cookie??蛻舳藶g覽器會(huì)把Cookie保存起來。當(dāng)瀏覽器再請(qǐng)求該網(wǎng)站時(shí),瀏覽器把請(qǐng)求的網(wǎng)址連同該Cookie一同提交給服務(wù)器。服務(wù)器檢查該Cookie,以此來辨認(rèn)用戶狀態(tài)。服務(wù)器還可以根據(jù)需要修改Cookie的內(nèi)容
Session:
Session是另一種記錄客戶狀態(tài)的機(jī)制,不同的是Cookie保存在客戶端瀏覽器中,而Session保存在服務(wù)器上。客戶端瀏覽器訪問服務(wù)器的時(shí)候,服務(wù)器把客戶端信息以某種形式記錄在服務(wù)器上。這就是Session??蛻舳藶g覽器再次訪問時(shí)只需要從該Session中查找該客戶的狀態(tài)就可以了,實(shí)質(zhì)上session就是保存在服務(wù)器端的鍵值對(duì)。
如果說Cookie機(jī)制是通過檢查客戶身上的“通行證”來確定客戶身份的話,那么Session機(jī)制就是通過檢查服務(wù)器上的“客戶明細(xì)表”來確認(rèn)客戶身份。Session相當(dāng)于程序在服務(wù)器上建立的一份客戶檔案,客戶來訪的時(shí)候只需要查詢客戶檔案表就可以了。
第一次請(qǐng)求,session的創(chuàng)建過程
在flask上下文中介紹了,請(qǐng)求到flask框架會(huì)執(zhí)行wsgi_app方法:
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) #實(shí)例化生成RequestContext對(duì)象 error = None try: try: ctx.push() #push上下文到LocalStack中 response = self.full_dispatch_request() #執(zhí)行視圖函數(shù)過程 except Exception as e: error = e response = self.handle_exception(e) #處理異常 except: error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error) # 刪除LocalStack中的數(shù)據(jù)
在改方法中會(huì)生成一個(gè)ctx也就是RequestContext對(duì)象:
class RequestContext(object): def __init__(self, app, environ, request=None): self.app = app # app對(duì)象 if request is None: request = app.request_class(environ) self.request = request # 封裝request self.url_adapter = app.create_url_adapter(self.request) self.flashes = None self.session = None #一開始的session
在這個(gè)對(duì)象中封裝了session,最初為None。接著在wsgi_app中執(zhí)行ctx.push:
def push(self): app_ctx = _app_ctx_stack.top # 獲取app上下文 if app_ctx is None or app_ctx.app != self.app: app_ctx = self.app.app_context() #將app上下文push到app_ctx對(duì)于的LocalStack中 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() _request_ctx_stack.push(self) if self.session is None: # 判斷session是否為None,一開始為None session_interface = self.app.session_interface # 獲取操作session的對(duì)象 self.session = session_interface.open_session( #調(diào)用open_session 創(chuàng)建session self.app, self.request ) if self.session is None: self.session = session_interface.make_null_session(self.app)
這里我們主要關(guān)注session,前面的代碼在上下文中已經(jīng)進(jìn)行了相關(guān)說明,這里有個(gè)判斷session是否為None,剛開始RequestContext中的session為None,所以條件成立,此時(shí)執(zhí)行以下語句:
session_interface = self.app.session_interface self.session = session_interface.open_session( self.app, self.request ) if self.session is None: self.session = session_interface.make_null_session(self.app)
首先來看session_interface = self.app.session_interface,self.app.session_interface就是app中的session_interface屬性:
session_interface = SecureCookieSessionInterface()
默認(rèn)是一個(gè)SecureCookieSessionInterface()對(duì)象,該對(duì)象的內(nèi)部主要實(shí)現(xiàn)了open_session和save_session用于使用和保存session。接著self.session被重新賦值為session_interface.open_session(self.app, self.request)方法返回的值,以下為open_session源碼:
def open_session(self, app, request): s = self.get_signing_serializer(app) #根據(jù)app.secret_key獲取簽名算法 if s is None: return None # 根據(jù)配置中的session_cookie_name獲取session對(duì)于的值 val = request.cookies.get(app.session_cookie_name) #如果request.cookies為空,val為空 if not val: return self.session_class() max_age = total_seconds(app.permanent_session_lifetime) try: data = s.loads(val, max_age=max_age) return self.session_class(data) except BadSignature: return self.session_class()
該方法返回self.session_class(),當(dāng)請(qǐng)求第一次來時(shí),request.cookies為None,所以val也為None,返回self.session_class(),而session_class又是SecureCookieSession:
session_class = SecureCookieSession
所以我們繼續(xù)看SecureCookieSession:
class SecureCookieSession(CallbackDict, SessionMixin): accessed = False def __init__(self, initial=None): def on_update(self): self.modified = True self.accessed = True super(SecureCookieSession, self).__init__(initial, on_update) def __getitem__(self, key): self.accessed = True return super(SecureCookieSession, self).__getitem__(key) def get(self, key, default=None): self.accessed = True return super(SecureCookieSession, self).get(key, default) def setdefault(self, key, default=None): self.accessed = True return super(SecureCookieSession, self).setdefault(key, default)
該類繼承了CallbackDict, SessionMixin我們繼續(xù)來看看CallbackDict:
class CallbackDict(UpdateDictMixin, dict): """A dict that calls a function passed every time something is changed. The function is passed the dict instance. """ def __init__(self, initial=None, on_update=None): dict.__init__(self, initial or ()) self.on_update = on_update def __repr__(self): return '<%s %s>' % ( self.__class__.__name__, dict.__repr__(self) )
也就是說SecureCookieSession繼承了CallbackDict而CallbackDict繼承了原生的dict,所以我們可以認(rèn)為SecureCookieSession是一個(gè)特殊的字典,是調(diào)用了SecureCookieSessionInterface類中open_session返回的特殊字典,經(jīng)過進(jìn)一步分析self.session此時(shí)就是這個(gè)字典,這也意味著session在執(zhí)行open_session方法時(shí)候被創(chuàng)建了,并保存在ctx中,也就是在RequestContext對(duì)象中,當(dāng)我們使用session時(shí)候是通過全局變量session = LocalProxy(partial(_lookup_req_object, 'session'))由LocalProxy對(duì)象從ctx中獲取到session。
第二次請(qǐng)求
開始我們知道session第一次請(qǐng)求來的時(shí)候是在open_session方法之后被創(chuàng)建,當(dāng)?shù)诙握?qǐng)求時(shí),此時(shí)在open_session方法中,val已經(jīng)不在是None,此時(shí)獲取cookie的有效時(shí)長(zhǎng),如果cookie依然有效,通過與寫入時(shí)同樣的簽名算法將cookie中的值解密出來并寫入字典并返回,若cookie已經(jīng)失效,則仍然返回'空字典',這樣以來在第二次請(qǐng)求中就能獲取到之前保存的session數(shù)據(jù)。
session生命周期
我們介紹了session創(chuàng)建時(shí)候是在ctx.push時(shí)候開始創(chuàng)建,也就是說在這之后我們就可以使用session,對(duì)它進(jìn)行操作了,那么session什么時(shí)候保存呢?我們接下來繼續(xù)看wsgi_app:
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) error = None try: try: ctx.push() response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)
生成session后,接著執(zhí)行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 """ self.try_trigger_before_first_request_functions() #執(zhí)行app.before_first_reques鉤子函數(shù) try: request_started.send(self) # 觸發(fā)request_started信號(hào) rv = self.preprocess_request() # 執(zhí)行before_request鉤子函數(shù) if rv is None: rv = self.dispatch_request() # 執(zhí)行視圖函數(shù) except Exception as e: rv = self.handle_user_exception(e) return self.finalize_request(rv)
這一部分先執(zhí)行鉤子app.before_first_reques在觸發(fā)request_started信號(hào),再執(zhí)行before_request鉤子函數(shù),然后在執(zhí)行視圖函數(shù),rv是執(zhí)行完視圖函數(shù)的返回值,最后執(zhí)行finalize_request,這里的session保存就發(fā)生在這里:
def finalize_request(self, rv, from_error_handler=False): response = self.make_response(rv) try: response = self.process_response(response) 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
注意這里的在最后會(huì)session判斷是否為空,會(huì)執(zhí)行save_session方法,也就是SecureCookieSessionInterface的save_session方法:
def save_session(self, app, session, response): domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) # If the session is modified to be empty, remove the cookie. # If the session is empty, return without setting the cookie. if not session: if session.modified: response.delete_cookie( app.session_cookie_name, domain=domain, path=path ) return # Add a "Vary: Cookie" header if the session was accessed at all. if session.accessed: response.vary.add('Cookie') if not self.should_set_cookie(app, session): return httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) samesite = self.get_cookie_samesite(app) expires = self.get_expiration_time(app, session) val = self.get_signing_serializer(app).dumps(dict(session)) response.set_cookie( app.session_cookie_name, val, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure, samesite=samesite )
該方法最后保存的session調(diào)用的response.set_cookie,其實(shí)是將數(shù)據(jù)保存在cookie中,也就是在客戶端的瀏覽器中,并非在服務(wù)端進(jìn)行數(shù)據(jù)的保存,當(dāng)請(qǐng)求完畢后會(huì)執(zhí)行ctx.auto_pop(error)這時(shí)候會(huì)從上下文中將session和request刪除,到此,session的生命周期結(jié)束。
視圖函數(shù)使用session
在介紹flask的上下文中就已經(jīng)對(duì)session進(jìn)行過介紹,其本質(zhì)也是通過LocalProxy操作上下文從而設(shè)置session,我們以session['username']='wd'作為列子,首先根據(jù)
session = LocalProxy(partial(_lookup_req_object, 'session'))
session是一個(gè)LocalProxy對(duì)象,執(zhí)行session['username']=‘wd'則執(zhí)行LocalProxy對(duì)象的__setitem__方法,而__setitem__方法中則是調(diào)用_get_current_object獲取ctx中的session對(duì)象,而其對(duì)象本質(zhì)是一個(gè)特殊的字典,相當(dāng)于在字典中加一對(duì)key,value。
小結(jié)
flask內(nèi)置session本質(zhì)上依靠上下文,當(dāng)請(qǐng)求到來時(shí),調(diào)用session_interface中的open_session方法解密獲取session的字典,并保存在RequestContext.session中,也就是上下文中,然后在視圖函數(shù)執(zhí)行完畢后調(diào)用session_interface的save_session方法,將session以加密的方式寫入response的cookie中,瀏覽器再保存數(shù)據(jù)。而第三方的session組件原理就是基于是open_session方法和save方法,從而實(shí)現(xiàn)session更多的session保存方案。
三、第三方組件flask-session
flask-session支持多種數(shù)據(jù)庫(kù)session保存方案如:redis、memchached、mongodb甚至文件系統(tǒng)等。官方文檔: https://pythonhosted.org/Flask-Session/
安裝:
pip3 install flask-session
redis
import redis from flask import Flask, session from flask_session import Session from datetime import timedelta app = Flask(__name__) app.debug = True app.secret_key = 'adavafa' app.config['SESSION_TYPE'] = 'redis' # session類型為redis app.config['SESSION_PERMANENT'] = True # 如果設(shè)置為True,則關(guān)閉瀏覽器session就失效。 app.config['PERMANENT_SESSION_LIFETIME']=timedelta(seconds=20) #一個(gè)持久化的會(huì)話的生存時(shí)間,是一個(gè)datetime.timedelta對(duì)象,也可以用一個(gè)整數(shù)來表示秒,前提設(shè)置了PERMANENT_SESSION_LIFETIME為True app.config['SESSION_USE_SIGNER'] = False # 是否對(duì)發(fā)送到瀏覽器上session的cookie值進(jìn)行加密,默認(rèn)False app.config['SESSION_KEY_PREFIX'] = 'flask-session' # 保存到redis中的key的前綴 app.config['SESSION_COOKIE_NAME']= 'session_id' # 保存在瀏覽器的cookie名稱 app.config['SESSION_REDIS'] = redis.Redis(host='10.1.210.33', port=‘6379',password=‘123123') # 用于連接redis的配置 #其他配置,不經(jīng)常使用 app.config['SESSION_COOKIE_DOMAIN']='127.0.0.1' # 設(shè)置cookie的域名,不建議設(shè)置默認(rèn)為server_name app.config['SESSION_COOKIE_PATH']='/' # 會(huì)話cookie的路徑。 如果未設(shè)置,則cookie將對(duì)所有url有效,默認(rèn)為'/' app.config['SESSION_COOKIE_HTTPONLY']=True # 是否啟動(dòng)httponly,默認(rèn)為true,為了防止xss腳本訪問cookie Session(app) @app.route('/login') def index(): session["username"]="jack" return 'login' if __name__ == '__main__': app.run()
Memchached
import memcache from flask import Flask, session from flask_session import Session from datetime import timedelta app = Flask(__name__) app.debug = True app.secret_key = 'adavafa' app.config['SESSION_TYPE'] = ‘memcached' # session類型為memcached app.config['SESSION_PERMANENT'] = True # 如果設(shè)置為True,則關(guān)閉瀏覽器session就失效。 app.config['PERMANENT_SESSION_LIFETIME']=timedelta(seconds=20) #一個(gè)持久化的會(huì)話的生存時(shí)間,是一個(gè)datetime.timedelta對(duì)象,也可以用一個(gè)整數(shù)來表示秒,前提設(shè)置了PERMANENT_SESSION_LIFETIME為True app.config['SESSION_USE_SIGNER'] = False # 是否對(duì)發(fā)送到瀏覽器上session的cookie值進(jìn)行加密,默認(rèn)False app.config['SESSION_KEY_PREFIX'] = 'flask-session' # 保存到緩存中的key的前綴 app.config['SESSION_COOKIE_NAME']= 'session_id' # 保存在瀏覽器的cookie名稱 app.config['SESSION_MEMCACHED'] = memcache.Client(['10.1.210.33:12000']) #連接 #其他配置,不經(jīng)常使用 app.config['SESSION_COOKIE_DOMAIN']='127.0.0.1' # 設(shè)置cookie的域名,不建議設(shè)置默認(rèn)為server_name app.config['SESSION_COOKIE_PATH']='/' # 會(huì)話cookie的路徑。 如果未設(shè)置,則cookie將對(duì)所有url有效,默認(rèn)為'/' app.config['SESSION_COOKIE_HTTPONLY']=True # 是否啟動(dòng)httponly,默認(rèn)為true,為了防止xss腳本訪問cookie Session(app) @app.route('/login') def index(): session["username"]="jack" return 'login' if __name__ == '__main__': app.run()
Filesystem
from flask import Flask, session from flask_session import Session from datetime import timedelta app = Flask(__name__) app.debug = True app.secret_key = 'adavafa' app.config['SESSION_TYPE'] = 'filesystem' # session類型為filesystem app.config['SESSION_FILE_DIR']='/opt/db' #文件保存目錄 app.config['SESSION_FILE_THRESHOLD'] = 300 # 存儲(chǔ)session的個(gè)數(shù)如果大于這個(gè)值時(shí),開始刪除 app.config['SESSION_PERMANENT'] = True # 如果設(shè)置為True,則關(guān)閉瀏覽器session就失效。 app.config['PERMANENT_SESSION_LIFETIME']=timedelta(seconds=20) #一個(gè)持久化的會(huì)話的生存時(shí)間,是一個(gè)datetime.timedelta對(duì)象,也可以用一個(gè)整數(shù)來表示秒,前提設(shè)置了PERMANENT_SESSION_LIFETIME為True app.config['SESSION_USE_SIGNER'] = False # 是否對(duì)發(fā)送到瀏覽器上session的cookie值進(jìn)行加密,默認(rèn)False app.config['SESSION_KEY_PREFIX'] = 'flask-session' # 保存到文件中的key的前綴 app.config['SESSION_COOKIE_NAME']= 'session_id' # 保存在瀏覽器的cookie名稱 #其他配置,不經(jīng)常使用 app.config['SESSION_COOKIE_DOMAIN']='127.0.0.1' # 設(shè)置cookie的域名,不建議設(shè)置默認(rèn)為server_name app.config['SESSION_COOKIE_PATH']='/' # 會(huì)話cookie的路徑。 如果未設(shè)置,則cookie將對(duì)所有url有效,默認(rèn)為'/' app.config['SESSION_COOKIE_HTTPONLY']=True # 是否啟動(dòng)httponly,默認(rèn)為true,為了防止xss腳本訪問cookie Session(app) @app.route('/login') def index(): session["username"]="jack" return 'login' if __name__ == '__main__': app.run()
mongodb
import pymongo from flask import Flask, session from flask_session import Session from datetime import timedelta app = Flask(__name__) app.debug = True app.secret_key = 'adavafa' app.config['SESSION_TYPE'] = 'mongodb' # session類型為mongodb app.config['SESSION_MONGODB'] = pymongo.MongoClient('localhost',27017) app.config['SESSION_MONGODB_DB'] = '數(shù)據(jù)庫(kù)名稱' app.config['SESSION_MONGODB_COLLECT'] = '表名稱' app.config['SESSION_PERMANENT'] = True # 如果設(shè)置為True,則關(guān)閉瀏覽器session就失效。 app.config['PERMANENT_SESSION_LIFETIME']=timedelta(seconds=20) #一個(gè)持久化的會(huì)話的生存時(shí)間,是一個(gè)datetime.timedelta對(duì)象,也可以用一個(gè)整數(shù)來表示秒,前提設(shè)置了PERMANENT_SESSION_LIFETIME為True app.config['SESSION_USE_SIGNER'] = False # 是否對(duì)發(fā)送到瀏覽器上session的cookie值進(jìn)行加密,默認(rèn)False app.config['SESSION_KEY_PREFIX'] = 'flask-session' # 保存的session的key的前綴 app.config['SESSION_COOKIE_NAME']= 'session_id' # 保存在瀏覽器的cookie名稱 #其他配置,不經(jīng)常使用 app.config['SESSION_COOKIE_DOMAIN']='127.0.0.1' # 設(shè)置cookie的域名,不建議設(shè)置默認(rèn)為server_name app.config['SESSION_COOKIE_PATH']='/' # 會(huì)話cookie的路徑。 如果未設(shè)置,則cookie將對(duì)所有url有效,默認(rèn)為'/' app.config['SESSION_COOKIE_HTTPONLY']=True # 是否啟動(dòng)httponly,默認(rèn)為true,為了防止xss腳本訪問cookie Session(app) @app.route('/login') def index(): session["username"]="jack" return 'login' if __name__ == '__main__': app.run()
sqlalchemy
import redis from flask import Flask, session from flask_session import Session from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.debug = True app.secret_key = 'adavafa' # 設(shè)置數(shù)據(jù)庫(kù)鏈接 app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:dev@127.0.0.1:3306/devops?charset=utf8' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True # 實(shí)例化SQLAlchemy db = SQLAlchemy(app) app.config['SESSION_TYPE'] = 'sqlalchemy' # session類型為sqlalchemy app.config['SESSION_SQLALCHEMY'] = db # SQLAlchemy對(duì)象 app.config['SESSION_SQLALCHEMY_TABLE'] = '表名' # session要保存的表名稱 app.config['SESSION_PERMANENT'] = True # 如果設(shè)置為True,則關(guān)閉瀏覽器session就失效。 app.config['PERMANENT_SESSION_LIFETIME']=timedelta(seconds=20) #一個(gè)持久化的會(huì)話的生存時(shí)間,是一個(gè)datetime.timedelta對(duì)象,也可以用一個(gè)整數(shù)來表示秒,前提設(shè)置了PERMANENT_SESSION_LIFETIME為True app.config['SESSION_USE_SIGNER'] = False # 是否對(duì)發(fā)送到瀏覽器上session的cookie值進(jìn)行加密,默認(rèn)False app.config['SESSION_KEY_PREFIX'] = 'flask-session' # 保存的session的key的前綴 app.config['SESSION_COOKIE_NAME']= 'session_id' # 保存在瀏覽器的cookie名稱 #其他配置,不經(jīng)常使用 app.config['SESSION_COOKIE_DOMAIN']='127.0.0.1' # 設(shè)置cookie的域名,不建議設(shè)置默認(rèn)為server_name app.config['SESSION_COOKIE_PATH']='/' # 會(huì)話cookie的路徑。 如果未設(shè)置,則cookie將對(duì)所有url有效,默認(rèn)為'/' app.config['SESSION_COOKIE_HTTPONLY']=True # 是否啟動(dòng)httponly,默認(rèn)為true,為了防止xss腳本訪問cookie Session(app) @app.route('/login') def index(): session["username"]="jack" return 'login' if __name__ == '__main__': app.run() ###使用SQLAlchemy時(shí)候先確保數(shù)據(jù)庫(kù)和表都存在 在命令行中創(chuàng)建表 #>>> from app import db #>>> db.create_all()
原理
這里以redis作為session存儲(chǔ)方案做分析,以下是RedisSessionInterface源碼:
class RedisSessionInterface(SessionInterface): serializer = pickle session_class = RedisSession def __init__(self, redis, key_prefix, use_signer=False, permanent=True): if redis is None: from redis import Redis redis = Redis() self.redis = redis self.key_prefix = key_prefix self.use_signer = use_signer self.permanent = permanent def open_session(self, app, request): sid = request.cookies.get(app.session_cookie_name) if not sid: sid = self._generate_sid() return self.session_class(sid=sid, permanent=self.permanent) if self.use_signer: signer = self._get_signer(app) if signer is None: return None try: sid_as_bytes = signer.unsign(sid) sid = sid_as_bytes.decode() except BadSignature: sid = self._generate_sid() return self.session_class(sid=sid, permanent=self.permanent) if not PY2 and not isinstance(sid, text_type): sid = sid.decode('utf-8', 'strict') val = self.redis.get(self.key_prefix + sid) if val is not None: try: data = self.serializer.loads(val) return self.session_class(data, sid=sid) except: return self.session_class(sid=sid, permanent=self.permanent) return self.session_class(sid=sid, permanent=self.permanent) def save_session(self, app, session, response): domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) if not session: if session.modified: self.redis.delete(self.key_prefix + session.sid) response.delete_cookie(app.session_cookie_name, domain=domain, path=path) return httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) expires = self.get_expiration_time(app, session) val = self.serializer.dumps(dict(session)) self.redis.setex(name=self.key_prefix + session.sid, value=val, time=total_seconds(app.permanent_session_lifetime)) if self.use_signer: session_id = self._get_signer(app).sign(want_bytes(session.sid)) else: session_id = session.sid response.set_cookie(app.session_cookie_name, session_id, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure)
分析:RedisSessionInterface繼承了SessionInterface
class SessionInterface(FlaskSessionInterface): def _generate_sid(self): return str(uuid4()) def _get_signer(self, app): if not app.secret_key: return None return Signer(app.secret_key, salt='flask-session', key_derivation='hmac')
而SessionInterface又繼承了FlaskSessionInterface,而FlaskSessionInterface又繼承了flask內(nèi)置的SessionInterface,并且RedisSessionInterface重寫了內(nèi)置session的open_session和save_session.
首先是RedisSessionInterface實(shí)例化用于初始化配置,例如redis的連接、簽名配置、過期配置、前綴配置等。
接下來看兩個(gè)核心方法:open_session方法和save_session方法。
open_session:
def open_session(self, app, request): sid = request.cookies.get(app.session_cookie_name) #獲取session id if not sid: #判斷session id是否為空,為空表示第一次請(qǐng)求 sid = self._generate_sid() # 返回使用uuid4隨機(jī)字符串 return self.session_class(sid=sid, permanent=self.permanent) if self.use_signer:# 判斷簽名配置 signer = self._get_signer(app) if signer is None: return None try: sid_as_bytes = signer.unsign(sid) # 對(duì)session id 進(jìn)行加密簽名 sid = sid_as_bytes.decode() except BadSignature: sid = self._generate_sid() return self.session_class(sid=sid, permanent=self.permanent) if not PY2 and not isinstance(sid, text_type): sid = sid.decode('utf-8', 'strict') val = self.redis.get(self.key_prefix + sid) # 獲取seession數(shù)據(jù) if val is not None: try: data = self.serializer.loads(val) # 反序列化數(shù)據(jù) return self.session_class(data, sid=sid) #返回 except: return self.session_class(sid=sid, permanent=self.permanent) return self.session_class(sid=sid, permanent=self.permanent)
改方法先從cookie中獲取session id,然后對(duì)session id判斷是否為空,為空表示第一次請(qǐng)求,則通過self._generate_sid()返回隨機(jī)字符串,作為返回給瀏覽器的sessionid
def _generate_sid(self): return str(uuid4())
接著判斷簽名判斷是否為true,然后對(duì)session 進(jìn)行簽名,這里和內(nèi)置session不同的是獲取session的時(shí)候通過self.redis.get(self.key_prefix + sid)在redis中進(jìn)行獲取。
save_session:
def save_session(self, app, session, response): domain = self.get_cookie_domain(app) # 獲取cookie中的域名 path = self.get_cookie_path(app) # 獲取cookie 中path if not session: # 判斷有誤session對(duì)象 if session.modified: #沒有但是被修改了,表示已經(jīng)被刪除了 self.redis.delete(self.key_prefix + session.sid) #清空session response.delete_cookie(app.session_cookie_name, domain=domain, path=path) return httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) expires = self.get_expiration_time(app, session) val = self.serializer.dumps(dict(session)) self.redis.setex(name=self.key_prefix + session.sid, value=val, time=total_seconds(app.permanent_session_lifetime)) #保存session if self.use_signer: session_id = self._get_signer(app).sign(want_bytes(session.sid)) else: session_id = session.sid response.set_cookie(app.session_cookie_name, session_id, # 設(shè)置cookie expires=expires, httponly=httponly, domain=domain, path=path, secure=secure)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python教程對(duì)函數(shù)中的參數(shù)進(jìn)行排序
這篇文章主要介紹了python教程對(duì)函數(shù)中的參數(shù)進(jìn)行排序的方法講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2021-09-09修改Python的pyxmpp2中的主循環(huán)使其提高性能
這篇文章主要介紹了修改Python的pyxmpp2中的主循環(huán)使其提高性能,pyxmpp2是Python中使用需XMPP協(xié)議的一個(gè)常用工具,要的朋友可以參考下2015-04-04Python產(chǎn)生batch數(shù)據(jù)的操作
這篇文章主要介紹了Python產(chǎn)生batch數(shù)據(jù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-03-03NumPy-ndarray 的數(shù)據(jù)類型用法說明
這篇文章主要介紹了NumPy-ndarray 的數(shù)據(jù)類型用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-05-05Python使用pptx實(shí)現(xiàn)復(fù)制頁面到其他PPT中
這篇文章主要為大家詳細(xì)介紹了python如何使用pptx庫(kù)實(shí)現(xiàn)從一個(gè)ppt復(fù)制頁面到另一個(gè)ppt里面,文中的示例代碼講解詳細(xì),感興趣的可以嘗試一下2023-02-02Python requests HTTP驗(yàn)證登錄實(shí)現(xiàn)流程
這篇文章主要介紹了Python requests HTTP驗(yàn)證登錄實(shí)現(xiàn)流程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11