Tornado的cookie過時問題

首先,web應用程序是使用HTTP協議進行數據傳輸,由於HTTP協議是無狀態的,因此一旦提交數據完成後,客戶端和服務器端的鏈接就會被關閉,再次進行數據的交換就得從新創建新的鏈接,那麼,有個問題就是服務器沒法經過鏈接來跟蹤用戶的會話。接下來,對於Session和Cookie這個技術就出來了。html

簡單介紹一下Session和Cookie:python

  Session:經過在服務器端記錄用戶信息從而來確認用戶身份,保存在服務器上,每一個用戶會話都有一個對應的sessionweb

  Cookie:經過在客戶端記錄信息確認身份,客戶端瀏覽器會把Cookie保存起來,當再次訪問的時候,把該Cookie一同提交給服務器,就能夠進行用戶的辨認了redis

 

工做時,須要對Tornado的Cookie設置過時時間,而後對於Tornado來講,查看源碼得知對Cookie的處理有4個方法。chrome

下面給出4個方法的源碼:瀏覽器

  版本說明: Tornado版本爲4.4.2緩存

a. get_cookie()服務器

def get_cookie(self, name, default=None):
    """Gets the value of the cookie with the given name, else default."""
    if self.request.cookies is not None and name in self.request.cookies:
        return self.request.cookies[name].value
    return default        

能夠從源碼註釋中容易的明白,其爲獲取cookie的方法,經過給予參數name後拿到cookie的值cookie

 

b. set_cookie()session

def set_cookie(self, name, value, domain=None, expires=None, path="/",
               expires_days=None, **kwargs):
    """Sets the given cookie name/value with the given options.

    Additional keyword arguments are set on the Cookie.Morsel
    directly.
    See http://docs.python.org/library/cookie.html#morsel-objects
    for available attributes.
    """
    # The cookie library only accepts type str, in both python 2 and 3
    name = escape.native_str(name)
    value = escape.native_str(value)
    if re.search(r"[\x00-\x20]", name + value):
        # Don't let us accidentally inject bad stuff
        raise ValueError("Invalid cookie %r: %r" % (name, value))
    if not hasattr(self, "_new_cookie"):
        self._new_cookie = Cookie.SimpleCookie()
    if name in self._new_cookie:
        del self._new_cookie[name]
    self._new_cookie[name] = value
    morsel = self._new_cookie[name]
    if domain:
        morsel["domain"] = domain
    if expires_days is not None and not expires:
        expires = datetime.datetime.utcnow() + datetime.timedelta(
            days=expires_days)
    if expires:
        morsel["expires"] = httputil.format_timestamp(expires)
    if path:
        morsel["path"] = path
    for k, v in kwargs.items():
        if k == 'max_age':
            k = 'max-age'

        # skip falsy values for httponly and secure flags because
        # SimpleCookie sets them regardless
        if k in ['httponly', 'secure'] and not v:
            continue

        morsel[k] = v

這個是對Cookie的設置方法,看到expires和expires_days彷佛就是咱們所要用到的過時時間的設置項

 

c. get_secure_cookie()

def get_secure_cookie(self, name, value=None, max_age_days=31,
                      min_version=None):
    """Returns the given signed cookie if it validates, or None.

    The decoded cookie value is returned as a byte string (unlike
    `get_cookie`).

    .. versionchanged:: 3.2.1

       Added the ``min_version`` argument.  Introduced cookie version 2;
       both versions 1 and 2 are accepted by default.
    """
    self.require_setting("cookie_secret", "secure cookies")
    if value is None:
        value = self.get_cookie(name)
    return decode_signed_value(self.application.settings["cookie_secret"],
                               name, value, max_age_days=max_age_days,
                               min_version=min_version)

這個get_secure_cookie()也是獲取cookie的方法,可是和上面的get_cookie()不一樣在於這個cookie值是一個加密的字符串,經過加密傳輸,可是get_cookie()是明文進行傳輸的,而後還注意到有一個參數 max_age_day=31,這個能夠結合下面set_secure_cookie進行理解

 

d. set_secure_cookie()

def set_secure_cookie(self, name, value, expires_days=30, version=None,
                      **kwargs):
    """Signs and timestamps a cookie so it cannot be forged.

    You must specify the ``cookie_secret`` setting in your Application
    to use this method. It should be a long, random sequence of bytes
    to be used as the HMAC secret for the signature.

    To read a cookie set with this method, use `get_secure_cookie()`.

    Note that the ``expires_days`` parameter sets the lifetime of the
    cookie in the browser, but is independent of the ``max_age_days``
    parameter to `get_secure_cookie`.

    Secure cookies may contain arbitrary byte values, not just unicode
    strings (unlike regular cookies)

    .. versionchanged:: 3.2.1

       Added the ``version`` argument.  Introduced cookie version 2
       and made it the default.
    """
    self.set_cookie(name, self.create_signed_value(name, value,
                                                   version=version),
                    expires_days=expires_days, **kwargs)

經過查看該方法,咱們能夠發現,其實該方法就是對第一個set_cookie的進一步封裝,經過註釋能夠知道:要使用這個方法對cookie進行設置,必須在項目的Application中,定義一個「cookie_secret」 --> 這個cookie_secret應該是隨機的,而且長字符串,固然你能夠設置成你本身定義的固定的字符串。而後還有一個參數expires_days特別須要值得注意,這裏若是咱們不進行設置的話,cookie的過時時間將爲30天,固然,這個跟get_secure_cookie中的max_age_day是相互獨立的。

接下來,咱們經過源碼分析後,咱們想對cookie設置過時時間,只須要在set_secure_cookie()函數中,將expires_day進行重寫就能夠了。

 

這裏須要注意一下幾點,是我在設置過程當中踩過的坑,記錄下來:

  1.這個設置過時時間的參數 expires_day,若是想以秒做爲過時單位的話,應該這樣寫:expires_day = time.time() + 對應的秒數

  2.若是想設置,關閉瀏覽器就清楚cookie的話,應該這樣設置,將過時時間重寫置空:expires_day = None

  3.這個問題,也是我遇到最蛋疼的問題,當時,我設置後,設置關閉瀏覽器清楚cookie一直沒法失效,還一度懷疑是tornado對於chorme瀏覽器的bug,以後發現瞭解決辦法,請看圖:

    

    就是由於chrome這個設置選項,默認是勾選了「關閉瀏覽器後繼續運行後臺應用」,因此我每次關閉瀏覽器打開後cookie永遠不會失效。(Ubuntu系統,不一樣ps去查看系統進程的話根本發現不了沒有關閉chrome的進程)

 

最後總結一下,Session和Cookie在整個項目中具體的設置和開發方法:

兩種狀態:

  1.對於登錄是會話級別的,就是在系統上面一直有操做的,能夠自定義cookie的過時時間

  2.對於客戶端來講,選擇關閉瀏覽器就清楚cookie

開發方法:

  1.對於登錄是會話級別,當用戶一直有操做的時候,咱們不能讓cookie直接過時,因此咱們在調用set_secure_cookie時,應該設置expires_day=None(關閉瀏覽器直接過時,若是expires_day後面有過時時間的話,會致使用戶在操做途中直接cookie過時而要求從新登錄);

  2.而後,咱們如何保持用戶在沒有操做的一段時間後進行過時呢? 這就要看咱們的記錄在服務器端的session了,通常咱們會把服務器端的session存儲在memcache、redis等緩存服務器中,而後設置相應的緩存過時時間,每次當用戶在系統上面有操做,有請求發生的時候,咱們就重寫緩存,重置session的過時時間,當用戶一段時間沒有操做的時候,存儲在緩存中的session就會過時被清除,當用戶再次發送請求後發現沒法找到對應的session,從而讓用戶沒法進行相應的操做客戶端會提示從新登錄。這樣子,就能夠實現當用戶有操做就不過時,沒有操做就在必定的時間斷內過時要求用戶從新登錄了。

相關文章
相關標籤/搜索