django中的cookies和session機制


0.概述

(1)為什么要有cookies和session

  在網站中,http請求是無狀態的。也就是說即使第一次和服務器連接后並且登錄成功后,第二次請求服務器依然不能知道當前請求是哪個用戶。而在現實網站中,那淘寶網來舉例,用戶將商品加入購物車和用戶付款是不同的請求,但這個這連個請求需要是同一個用戶,而這樣的使用場景很多。總結來說,cookies和session一種用戶識別用戶身份的解決方案。

(2)cookies

  第一次登錄后服務器返回一些數據(cookie)給瀏覽器,然后瀏覽器保存在本地,當該用戶發送第二次請求的時候,就會自動的把上次請求存儲的 cookie 數據自動的攜帶給服務器,服務器通過瀏覽器攜帶的數據就能判斷當前用戶是哪個了。 cookie 存儲的數據量有限,不同的瀏覽器有不同的存儲大小,但一般不超過4KB。因此使用 cookie 只能存儲一些小量的數據。

(3)session

  session和cookie的作用有點類似,都是為了存儲用戶相關的信息。不同的是, cookie 是存儲在本地瀏覽器, session 是一個思路、一個概念、一個服務器存儲授權信息的解決方案,不同的服務器,不同的框架,不同的語言有不同的實現。雖然實現不一樣,但是他們的目的都是服務器為了方便存儲數據的。 session 的出現,是為了解決 cookie 存儲數據不安全的問題的。

(4)cookies與session對比

1.存儲位置。cookies存儲在服務端,session存儲在服務端。
2.安全性。session因為存儲在服務端,安全性更高。
3.存儲容量。cookies一般不能超過4kb,而session沒有限制。

1.操作cookies

cookies本質上是返回值給瀏覽器,而在Django上是通過調用response對象的方法來實現cookies的操作

(1)設置cookies

通過set_cookies方法來實現的。具體參數如下:

1.  key  :這個 cookie  的 key  。
2.  value  :這個 cookie  的 value  。
3.  max_age  :最長的生命周期。單位是秒。
4.  expires  :過期時間。跟 max_age  是類似的,只不過這個參數需要傳遞一個具體的日期,比如 datetime  或者是符合日期格式的字符串。如果同時設置了 expires  和 max_age  ,那么將
   會使用 expires  的值作為過期時間。
5.  path  :對域名下哪個路徑有效。默認是對域名下所有路徑都有效。
6.  domain  :針對哪個域名有效。默認是針對主域名下都有效,如果只要針對某個子域名才有效,那么可以設置這個屬性.
7.  secure  :是否是安全的,如果設置為 True  ,那么只能在 https  協議下才可用。
8.  httponly  :默認是 False  。如果為 True  ,那么在客戶端不能通過 JavaScript  進行操作。

(2)刪除cookie:

  通過 delete_cookie方法即可刪除 cookie 。實際上刪除 cookie 就是將指定的 cookie 的值設置為空的字符串,然后使用將他的過期時間設置為 0 ,也就是瀏覽器關閉后就過期。

(3)獲取cookie:

  獲取瀏覽器發送過來的 cookie  信息。可以通過 request.COOKIES(最終對象是一個字典類型,可以通過遍歷來獲取所需要的數據)。

2.操作sessions

(1)session存儲方案介紹

存儲在服務端:通過 cookie  存儲一個 sessionid  ,然后具體的數據則是保存在 session  中。如果用戶已經登錄,則服務器會在 cookie  中保存一個 sessionid,
下次再次請求的時候,會把該 sessionid 攜帶上來,服務器根據 sessionid 在 session 庫中獲取用戶的 session 數據。就能知道該用戶到底是誰,以及之前保存的一些狀態信息。
這種專業術語叫做 server side session 。Django 把 session 信息默認存儲到數據庫中,當然也可以存儲到其他地方,比如緩存中,文件系統中等。
存儲在服務器的數據會更加的安全,不容易被竊取。但存儲在服務器也有一定的弊端,就是會占用服務器的資源,但現在服務器已經發展至今,一些 session 信息還是綽綽有余的。

存儲在瀏覽器:將 session 數據加密,然后存儲在 cookie 中。這種專業術語叫做 client side session。flask框架默認采用的就是這種方式,但是也可以替換成其他形式。

(2)sessionss操作流程:

  django  中的 session  默認情況下是存儲在服務器的數據庫中的,通過 request.session  即可操作。常用方法如下:

1.  get  :用來從 session  中獲取指定值。
2.  pop  :從 session  中刪除一個值。
3.  keys  :從 session  中獲取所有的鍵。
4.  items  :從 session  中獲取所有的值。
5.  clear  :清除當前這個用戶的 session  數據。
6.  flush  :刪除 session  並且刪除在瀏覽器中存儲的 session_id  ,一般在注銷的時候用得比較多。
7.  set_expiry(value)  :設置過期時間。
    整形:代表秒數,表示多少秒后過期。
    0  :代表只要瀏覽器關閉, session  就會過期。
    None  :會使用全局的 session  配置。在 settings.py  中可以設置 SESSION_COOKIE_AGE  來配置全局的過期時間。默認是 1209600  秒,也就是2周的時間。
8.  clear_expired:清除過期的session。 Django並不會清除過期的session,需要定期手動的清理,或者是在終端,使用命令行 python manage.py clearsessions來清除過期
  的 session  。

(3)session存儲機制修改

  默認情況下, session 數據是存儲到數據庫中的。當然也可以將 session 數據存儲到其他地方。可以通過設置 SESSION_ENGINE 來更改 session 的存儲位置,這個可以配置為以下幾種方案:

1.  django.contrib.sessions.backends.db  :使用數據庫。默認就是這種方案。
2.  django.contrib.sessions.backends.file  :使用文件來存儲session。
3.  django.contrib.sessions.backends.cache  :使用緩存來存儲session。想要將數據存儲到緩存中,前提是你必須要在 settings.py 中配置好 CACHES ,
  並且是需要使用 Memcached ,而不能使用純內存作為緩存。
4. django.contrib.sessions.backends.cached_db :在存儲數據的時候,會將數據先存到緩存中,再存到數據庫中。這樣就可以保證萬一緩存系統出現問題,session數據也不會丟失。在   獲取數據的時候,會先從緩存中獲取,如果緩存中沒有,那么就會從數據庫中獲取。 5. django.contrib.sessions.backends.signed_cookies :將 session 信息加密后存儲到瀏覽器的 cookie 中。這種方式要注意安全,
  建議設置SESSION_COOKIE_HTTPONLY
=True ,那么在瀏覽器中不能通過 js 來操作 session 數據,並且還需要對 settings.py 中的 SECRET_KEY 進行保密,
  因為一旦別人知道這個 SECRET_KEY ,那么就可以進行解密。另外還有就是在 cookie 中,存儲的數據不能超過 4k 。

3.總結

一個好的程序員不應該只是知道不要重復造輪子,但是應該知道輪子是如何造的,了解一個功能背后的原理,能夠設計出適合自己的輪子,這才是優秀的程序員。所謂舉一反三,從設計者的角度來思考。要知道代碼只是結局問題的一種表達方式,你可以有自己的表達方式。

3.1django中cookies實現原理

(1)cookies的設置流程:

1.客戶端發起一個請求連接(如HTTP GET)。
2.服務器在http響應頭上加上Set-Cookie,里面存放字符串的鍵值對。
3.客戶端隨后的http請求頭加上Cookie首部,它包含了之前服務器響應中設置cookie的信息。

(2)利用Python實現cookies設置,代碼如下:

from BaseHTTPServer import HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
import Cookie

class MyRequestHandler(SimpleHTTPRequestHandler):
    def do_GET(self):
        content = "Path is: %s" % self.path
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.send_header('Content-length', str(len(content)))

        cookie = Cookie.SimpleCookie()
        cookie['id'] = 'some_value_42'

        self.wfile.write(cookie.output())
        self.wfile.write('\r\n')

        self.end_headers()
        self.wfile.write(content)

server = HTTPServer(('', 59900), MyRequestHandler)
server.serve_forever()

(3)Django通過一系列的包裝使得封裝Cookie的操作變得更加簡單,僅僅使用set_cookies就可以操作,那么它在其中是怎么實現cookie的讀取的呢,下面來窺探原理。

def _get_cookies(self):
    if not hasattr(self, '_cookies'):
        self._cookies = http.parse_cookie(self.environ.get('HTTP_COOKIE', ''))
    return self._cookies

可以看出,獲取cookie的操作用了Lazy initialization(延遲加載)的技術,因為如果客戶端不需要用到cookie,這個過程只會浪費不必要的操作。

再來看parse_cookie的實現:

def parse_cookie(cookie):
    if cookie == '':
        return {}
    if not isinstance(cookie, Cookie.BaseCookie):
        try:
            c = SimpleCookie()
            c.load(cookie, ignore_parse_errors=True)
        except Cookie.CookieError:
            # 無效cookie
            return {}
    else:
        c = cookie
    cookiedict = {}
    for key in c.keys():
        cookiedict[key] = c.get(key).value
    return cookiedict

它負責解析Cookie並把結果集成到一個dict(字典)對象中,並返回字典。而設置cookie的操作則會被WSGIHandler執行。

3.2session原理分析

(1)Django中的session實現

class SessionBase(object):
    """
    Base class for all Session classes.
    """
    TEST_COOKIE_NAME = 'testcookie'
    TEST_COOKIE_VALUE = 'worked'

    def __init__(self, session_key=None):
        self._session_key = session_key
        self.accessed = False
        self.modified = False
        self.serializer = import_string(settings.SESSION_SERIALIZER)

其實django中的session就是一個模擬dict的對象,並實現了一系列的hash和序列化方法,默認持久化在數據庫中(有時候也可能由於為了提高性能,用redis之類的內存數據庫來緩存session)。

(2)session的操作機制

session操作是通過request.session再加上系列的方法實現的,那么其背后的原理本質上是什么呢?

其實是用了Django的中間件部分,可以參考后續博客內容:django的中間件和上下文處理器。這里先簡單介紹,中間件可以在請求到來之前和響應返回的時候做一些處理。

我們看傳統的django視圖模式一般是這樣的:http請求->view->http響應,而加入中間件框架后,則變為:http請求->中間件處理->app->中間件處理->http響應。而在django中這兩個處理分別對應process_request和process_response函數,這兩個鈎子函數將會在特定的時候被觸發。

下面結合session中間件具體分析:

class SessionMiddleware(object):
    def __init__(self):
        engine = import_module(settings.SESSION_ENGINE)
        self.SessionStore = engine.SessionStore

    def process_request(self, request):
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
        request.session = self.SessionStore(session_key)

    def process_response(self, request, response):
        """
        If request.session was modified, or if the configuration is to save the
        session every time, save the changes and set a session cookie or delete
        the session cookie if the session has been emptied.
        """
        try:
            accessed = request.session.accessed
            modified = request.session.modified
            empty = request.session.is_empty()
        except AttributeError:
            pass
        else:
            # First check if we need to delete this cookie.
            # The session should be deleted only if the session is entirely empty
            if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
                response.delete_cookie(settings.SESSION_COOKIE_NAME,
                    domain=settings.SESSION_COOKIE_DOMAIN)
            else:
                if accessed:
                    patch_vary_headers(response, ('Cookie',))
                if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
                    if request.session.get_expire_at_browser_close():
                        max_age = None
                        expires = None
                    else:
                        max_age = request.session.get_expiry_age()
                        expires_time = time.time() + max_age
                        expires = cookie_date(expires_time)
                    # Save the session data and refresh the client cookie.
                    # Skip session save for 500 responses, refs #3881.
                    if response.status_code != 500:
                        try:
                            request.session.save()
                        except UpdateError:
                            # The user is now logged out; redirecting to same
                            # page will result in a redirect to the login page
                            # if required.
                            return redirect(request.path)
                        response.set_cookie(settings.SESSION_COOKIE_NAME,
                                request.session.session_key, max_age=max_age,
                                expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
                                path=settings.SESSION_COOKIE_PATH,
                                secure=settings.SESSION_COOKIE_SECURE or None,
                                httponly=settings.SESSION_COOKIE_HTTPONLY or None)
        return response

在請求到來后,SessionMiddleware的process_request在請求取出session_key,並把一個新的session對象賦給request.session,而在返回響應時,process_response則判斷session是否被修改或過期,來更新session的信息。

(3)dajngo認證中的session

其實request.user的實現也借助到了session。當用戶通過login(request,user)登陸的時候,會默認在數據庫中保存session數據。

跟上面提到的Session中間件相似,用戶驗證也有一個中間件:AuthenticationMiddleware,在process_request中,通過request.class.user = LazyUser()在request設置了一個全局的可緩存的用戶對象。

class LazyUser(object):
    def __get__(self, request, obj_type=None):
        if not hasattr(request, '_cached_user'):
            from django.contrib.auth import get_user
            request._cached_user = get_user(request)
        return request._cached_user

class AuthenticationMiddleware(object):
    def process_request(self, request):
        request.__class__.user = LazyUser()
        return None

在get_user里,會在檢查session中是否存放了當前用戶對應的user_id,如果有,則通過id在model查找相應的用戶返回,否則返回一個匿名的用戶對象(AnonymousUser)。

def get_user(request):
    from django.contrib.auth.models import AnonymousUser
    try:
        user_id = request.session[SESSION_KEY]
        backend_path = request.session[BACKEND_SESSION_KEY]
        backend = load_backend(backend_path)
        user = backend.get_user(user_id) or AnonymousUser()
    except KeyError:
        user = AnonymousUser()
    return user

 

參考博客:django 中 session 的實現機制


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM