Flask框架【七】—session組件詳解

1、flask session簡介

flask中session組件可分爲內置的session組件還有第三方flask-session組件,內置的session組件缺點:python

  • 功能單一mysql

  • session是保存在瀏覽器中的cookie中,不安全,redis

  • 大小有限制算法

而第三方插件flask-session可支持redis、memcached、文本等session的存儲。sql

2、內置session處理機制

Cookie與Session

咱們回顧一下cookie和session知識mongodb

Cookie

Cookie意爲「甜餅」,是由W3C組織提出,最先由Netscape社區發展的一種機制。目前Cookie已經成爲標準,全部的主流瀏覽器如IE、Netscape、Firefox、Opera等都支持Cookie。因爲HTTP是一種無狀態的協議,服務器單從網絡鏈接上無從知道客戶身份。怎麼辦呢?就給客戶端們頒發一個通行證吧,每人一個,不管誰訪問都必須攜帶本身通行證。這樣服務器就能從通行證上確認客戶身份了。這就是Cookie的工做原理。數據庫

Cookie其實是一小段的文本信息。客戶端請求服務器,若是服務器須要記錄該用戶狀態,就使用response向客戶端瀏覽器頒發一個Cookie。客戶端瀏覽器會把Cookie保存起來。當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該Cookie一同提交給服務器。服務器檢查該Cookie,以此來辨認用戶狀態。服務器還能夠根據須要修改Cookie的內容flask

Session

Session是另外一種記錄客戶狀態的機制,不一樣的是Cookie保存在客戶端瀏覽器中,而Session保存在服務器上。客戶端瀏覽器訪問服務器的時候,服務器把客戶端信息以某種形式記錄在服務器上。這就是Session。客戶端瀏覽器再次訪問時只須要從該Session中查找該客戶的狀態就能夠了,實質上session就是保存在服務器端的鍵值對。瀏覽器

若是說Cookie機制是經過檢查客戶身上的「通行證」來肯定客戶身份的話,那麼Session機制就是經過檢查服務器上的「客戶明細表」來確認客戶身份。Session至關於程序在服務器上創建的一份客戶檔案,客戶來訪的時候只須要查詢客戶檔案表就能夠了。緩存

session流程

1.第一次請求,session的建立過程

flask上下文中介紹了,請求到flask框架會執行wsgi_app方法:

def wsgi_app(self, environ, start_response):
    ctx = self.request_context(environ)  # 實例化生成RequestContext對象
    error = None
    try:
        try:
            ctx.push()  # push上下文到LocalStack中
            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)  # 刪除LocalStack中的數據

在改方法中會生成一個ctx也就是RequestContext對象:

class RequestContext(object):
    def __init__(self, app, environ, request=None):
        self.app = app  # app對象
        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

在這個對象中封裝了session,最初爲None。接着在wsgi_app中執行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對於的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的對象
        self.session = session_interface.open_session(  # 調用open_session 建立session
            self.app, self.request
        )
        if self.session is None:
            self.session = session_interface.make_null_session(self.app)

這裏咱們主要關注session,前面的代碼在上下文中已經進行了相關說明,這裏有個判斷session是否爲None,剛開始RequestContext中的session爲None,因此條件成立,此時執行如下語句:

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()

默認是一個SecureCookieSessionInterface()對象,該對象的內部主要實現了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)  # 根據app.secret_key獲取簽名算法
    if s is None:
        return None
    # 根據配置中的session_cookie_name獲取session對於的值
    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(),當請求第一次來時,request.cookies爲None,因此val也爲None,返回self.session_class(),而session_class又是SecureCookieSession:

session_class = SecureCookieSession

因此咱們繼續看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咱們繼續來看看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,因此咱們能夠認爲SecureCookieSession是一個特殊的字典,是調用了SecureCookieSessionInterface類中open_session返回的特殊字典,通過進一步分析self.session此時就是這個字典,這也意味着session在執行open_session方法時候被建立了,並保存在ctx中,也就是在RequestContext對象中,當咱們使用session時候是經過全局變量session = LocalProxy(partial(_lookup_req_object, 'session'))由LocalProxy對象從ctx中獲取到session。

2.第二次請求

開始咱們知道session第一次請求來的時候是在open_session方法以後被建立,當第二次請求時,此時在open_session方法中,val已經不在是None,此時獲取cookie的有效時長,若是cookie依然有效,經過與寫入時一樣的簽名算法將cookie中的值解密出來並寫入字典並返回,若cookie已經失效,則仍然返回'空字典',這樣以來在第二次請求中就能獲取到以前保存的session數據。

session生命週期

咱們介紹了session建立時候是在ctx.push時候開始建立,也就是說在這以後咱們就可使用session,對它進行操做了,那麼session何時保存呢?咱們接下來繼續看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後,接着執行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()  # 執行app.before_first_reques鉤子函數
    try:
        request_started.send(self)  # 觸發request_started信號
        rv = self.preprocess_request()  # 執行before_request鉤子函數
        if rv is None:
            rv = self.dispatch_request()  # 執行視圖函數
    except Exception as e:
        rv = self.handle_user_exception(e)
    return self.finalize_request(rv)

這一部分先執行鉤子app.before_first_reques在觸發request_started信號,再執行before_request鉤子函數,而後在執行視圖函數,rv是執行完視圖函數的返回值,最後執行finalize_request,這裏的session保存就發生在這裏:

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

注意這裏的在最後會session判斷是否爲空,會執行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調用的response.set_cookie,實際上是將數據保存在cookie中,也就是在客戶端的瀏覽器中,並不是在服務端進行數據的保存,當請求完畢後會執行ctx.auto_pop(error)這時候會從上下文中將session和request刪除,到此,session的生命週期結束。

視圖函數使用session

在介紹flask的上下文中就已經對session進行過介紹,其本質也是經過LocalProxy操做上下文從而設置session,咱們以session['username']='wd'做爲列子,首先根據

session = LocalProxy(partial(_lookup_req_object, 'session'))

session是一個LocalProxy對象,執行session['username']=‘wd'則執行LocalProxy對象的setitem方法,而setitem方法中則是調用_get_current_object獲取ctx中的session對象,而其對象本質是一個特殊的字典,至關於在字典中加一對key,value。

小結

flask內置session本質上依靠上下文,當請求到來時,調用session_interface中的open_session方法解密獲取session的字典,並保存在RequestContext.session中,也就是上下文中,而後在視圖函數執行完畢後調用session_interface的save_session方法,將session以加密的方式寫入response的cookie中,瀏覽器再保存數據。而第三方的session組件原理就是基因而open_session方法和save方法,從而實現session更多的session保存方案。

3、第三方組件flask-session

flask-session支持多種數據庫session保存方案如:redis、memchached、mongodb甚至文件系統等。官方文檔: 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  # 若是設置爲True,則關閉瀏覽器session就失效。
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(seconds=20)
# 一個持久化的會話的生存時間,是一個datetime.timedelta對象,也能夠用一個整數來表示秒,前提設置了PERMANENT_SESSION_LIFETIME爲True
app.config['SESSION_USE_SIGNER'] = False  # 是否對發送到瀏覽器上session的cookie值進行加密,默認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的配置
# 其餘配置,不常用
app.config['SESSION_COOKIE_DOMAIN'] = '127.0.0.1'  # 設置cookie的域名,不建議設置默認爲server_name
app.config['SESSION_COOKIE_PATH'] = '/'  # 會話cookie的路徑。 若是未設置,則cookie將對全部url有效,默認爲'/'
app.config['SESSION_COOKIE_HTTPONLY'] = True  # 是否啓動httponly,默認爲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  # 若是設置爲True,則關閉瀏覽器session就失效。
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(seconds=20)
# 一個持久化的會話的生存時間,是一個datetime.timedelta對象,也能夠用一個整數來表示秒,前提設置了PERMANENT_SESSION_LIFETIME爲True
app.config['SESSION_USE_SIGNER'] = False  # 是否對發送到瀏覽器上session的cookie值進行加密,默認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'])  # 鏈接

# 其餘配置,不常用
app.config['SESSION_COOKIE_DOMAIN'] = '127.0.0.1'  # 設置cookie的域名,不建議設置默認爲server_name
app.config['SESSION_COOKIE_PATH'] = '/'  # 會話cookie的路徑。 若是未設置,則cookie將對全部url有效,默認爲'/'
app.config['SESSION_COOKIE_HTTPONLY'] = True  # 是否啓動httponly,默認爲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  # 存儲session的個數若是大於這個值時,開始刪除

app.config['SESSION_PERMANENT'] = True  # 若是設置爲True,則關閉瀏覽器session就失效。
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(seconds=20)
# 一個持久化的會話的生存時間,是一個datetime.timedelta對象,也能夠用一個整數來表示秒,前提設置了PERMANENT_SESSION_LIFETIME爲True
app.config['SESSION_USE_SIGNER'] = False  # 是否對發送到瀏覽器上session的cookie值進行加密,默認False
app.config['SESSION_KEY_PREFIX'] = 'flask-session'  # 保存到文件中的key的前綴
app.config['SESSION_COOKIE_NAME'] = 'session_id'  # 保存在瀏覽器的cookie名稱

# 其餘配置,不常用
app.config['SESSION_COOKIE_DOMAIN'] = '127.0.0.1'  # 設置cookie的域名,不建議設置默認爲server_name
app.config['SESSION_COOKIE_PATH'] = '/'  # 會話cookie的路徑。 若是未設置,則cookie將對全部url有效,默認爲'/'
app.config['SESSION_COOKIE_HTTPONLY'] = True  # 是否啓動httponly,默認爲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'] = '數據庫名稱'
app.config['SESSION_MONGODB_COLLECT'] = '表名稱'

app.config['SESSION_PERMANENT'] = True  # 若是設置爲True,則關閉瀏覽器session就失效。
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(seconds=20)
# 一個持久化的會話的生存時間,是一個datetime.timedelta對象,也能夠用一個整數來表示秒,前提設置了PERMANENT_SESSION_LIFETIME爲True
app.config['SESSION_USE_SIGNER'] = False  # 是否對發送到瀏覽器上session的cookie值進行加密,默認False
app.config['SESSION_KEY_PREFIX'] = 'flask-session'  # 保存的session的key的前綴
app.config['SESSION_COOKIE_NAME'] = 'session_id'  # 保存在瀏覽器的cookie名稱

# 其餘配置,不常用
app.config['SESSION_COOKIE_DOMAIN'] = '127.0.0.1'  # 設置cookie的域名,不建議設置默認爲server_name
app.config['SESSION_COOKIE_PATH'] = '/'  # 會話cookie的路徑。 若是未設置,則cookie將對全部url有效,默認爲'/'
app.config['SESSION_COOKIE_HTTPONLY'] = True  # 是否啓動httponly,默認爲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'

# 設置數據庫連接
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:dev@127.0.0.1:3306/devops?charset=utf8'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# 實例化SQLAlchemy
db = SQLAlchemy(app)
app.config['SESSION_TYPE'] = 'sqlalchemy'  # session類型爲sqlalchemy
app.config['SESSION_SQLALCHEMY'] = db  # SQLAlchemy對象
app.config['SESSION_SQLALCHEMY_TABLE'] = '表名'  # session要保存的表名稱

app.config['SESSION_PERMANENT'] = True  # 若是設置爲True,則關閉瀏覽器session就失效。
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(seconds=20)
# 一個持久化的會話的生存時間,是一個datetime.timedelta對象,也能夠用一個整數來表示秒,前提設置了PERMANENT_SESSION_LIFETIME爲True
app.config['SESSION_USE_SIGNER'] = False  # 是否對發送到瀏覽器上session的cookie值進行加密,默認False
app.config['SESSION_KEY_PREFIX'] = 'flask-session'  # 保存的session的key的前綴
app.config['SESSION_COOKIE_NAME'] = 'session_id'  # 保存在瀏覽器的cookie名稱

# 其餘配置,不常用
app.config['SESSION_COOKIE_DOMAIN'] = '127.0.0.1'  # 設置cookie的域名,不建議設置默認爲server_name
app.config['SESSION_COOKIE_PATH'] = '/'  # 會話cookie的路徑。 若是未設置,則cookie將對全部url有效,默認爲'/'
app.config['SESSION_COOKIE_HTTPONLY'] = True  # 是否啓動httponly,默認爲true,爲了防止xss腳本訪問cookie

Session(app)


@app.route('/login')
def index():
    session["username"] = "jack"
    return 'login'


if __name__ == '__main__':
    app.run()

###使用SQLAlchemy時候先確保數據庫和表都存在在命令行中建立表
# >>> from app import db
# >>> db.create_all()

原理

這裏以redis做爲session存儲方案作分析,如下是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內置的SessionInterface,而且RedisSessionInterface重寫了內置session的open_session和save_session.

首先是RedisSessionInterface實例化用於初始化配置,例如redis的鏈接、簽名配置、過時配置、前綴配置等。

接下來看兩個核心方法: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是否爲空,爲空表示第一次請求
        sid = self._generate_sid()  # 返回使用uuid4隨機字符串
        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)  # 對session id 進行加密簽名
            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數據
    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)

改方法先從cookie中獲取session id,而後對session id判斷是否爲空,爲空表示第一次請求,則經過self._generate_sid()返回隨機字符串,做爲返回給瀏覽器的sessionid

def _generate_sid(self):
    return str(uuid4())

接着判斷簽名判斷是否爲true,而後對session 進行簽名,這裏和內置session不一樣的是獲取session的時候經過self.redis.get(self.key_prefix + sid)在redis中進行獲取。

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對象
        if session.modified:  # 沒有可是被修改了,表示已經被刪除了
            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,  # 設置cookie
                        expires=expires, httponly=httponly,
                        domain=domain, path=path, secure=secure)
相關文章
相關標籤/搜索