欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

flask session組件的使用示例

 更新時(shí)間:2018年12月25日 17:28:02   作者:W-D  
這篇文章主要介紹了flask session組件的使用示例,詳細(xì)介紹內(nèi)置session以及第三方session組件的使用方法以及處理機(jī)制,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

一、簡(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)文章

最新評(píng)論