Django Cookie與Session

HammerZe發表於2022-03-10

Django Cookie與Session

python Django 的影像結果

Django cookie

cookie由來

以前的網站都是靜態的,早期的論壇,新聞網頁都是不需要登入的,但是隨著發展,動態網站的誕生需要登入使用,我們知道HTTP協議的四大特性之一是無狀態,就是每次請求都是獨立的,它的執行情況和結果與前面的請求和之後的請求都無直接關係,它不會受前面的請求響應情況直接影響,也不會直接影響後面的請求響應情況;

狀態可以理解為客戶端和伺服器在某次會話中產生的資料,那無狀態的就以為這些資料不會被保留。會話中產生的資料又是我們需要儲存的,也就是說要“保持狀態”。因此Cookie就是在這樣一個場景下誕生

cookie介紹

Cookie的結構是key-value結構,類似於python中的字典,隨著伺服器端的響應傳送給客戶端瀏覽器。然後客戶端瀏覽器會把Cookie儲存起來,當下一次再訪問伺服器時把Cookie再傳送給伺服器。 Cookie是由伺服器建立,然後通過響應傳送給客戶端的一個鍵值對。客戶端會儲存Cookie,並會標註出Cookie的來源(哪個伺服器的Cookie)。當客戶端向伺服器發出請求時會把所有這個伺服器Cookie包含在請求中傳送給伺服器,這樣伺服器就可以識別客戶端了;

cookie的工作原理是:由伺服器產生內容,瀏覽器收到請求後儲存在本地;當瀏覽器再次訪問時,瀏覽器會自動帶上Cookie,這樣伺服器就能通過Cookie的內容來判斷這個是“誰”了;

cookie規範

  • Cookie大小上限為4KB;
  • 一個伺服器最多在客戶端瀏覽器上儲存20個Cookie;
  • 一個瀏覽器最多儲存300個Cookie;

上面的資料只是HTTP的Cookie規範,但在瀏覽器大戰的今天,一些瀏覽器為了打敗對手,為了展現自己的能力起見,可能對Cookie規範“擴充套件”了一些,例如每個Cookie的大小為8KB,最多可儲存500個Cookie等!但也不會出現把你硬碟佔滿的可能!
注意,不同瀏覽器之間是不共享Cookie的。也就是說在你使用IE訪問伺服器時,伺服器會把Cookie發給IE,然後由IE儲存起來,當你在使用FireFox訪問伺服器時,不可能把IE儲存的Cookie傳送給伺服器;

如果伺服器端傳送重複的Cookie那麼會覆蓋原有的Cookie,例如客戶端的第一個請求伺服器端傳送的Cookie是:Set-Cookie: a=A;第二請求伺服器端傳送的是:Set-Cookie: a=AA,那麼客戶端只留下一個Cookie,即:a=AA;

image

cookie設定相關引數

  • key, 鍵
  • value='', 值
  • max_age=None, 超時時間 cookie需要延續的時間(以秒為單位)如果引數是\ None`` ,這個cookie會延續到瀏覽器關閉為止
  • expires=None, 超時時間(IE requires expires, so set it if hasn't been already.)
  • path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie可以被任何url的頁面訪問,瀏覽器只會把cookie回傳給帶有該路徑的頁面,這樣可以避免將cookie傳給站點中的其他的應用。
  • domain=None, Cookie生效的域名 你可用這個引數來構造一個跨站cookie。如, domain=".example.com"所構造的cookie對下面這些站點都是可讀的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果該引數設定為 None ,cookie只能由設定它的站點讀取
  • secure=False, 瀏覽器將通過HTTPS來回傳cookie
  • httponly=False 只能http協議傳輸,無法被JavaScript獲取(不是絕對,底層抓包可以獲取到也可以被覆蓋)
rep = HttpResponse(...)
# 普通設定
rep.set_cookie(key,value)
# 加鹽設定
rep.set_signed_cookie(key,value,salt='加密鹽')
# 設定過期時間,單位是秒
rep.set_signed_cookie(key,value,salt='加密鹽',expires=3600)

cookie獲取

# 獲取cookie
request.COOKIES['key']
request.COOKIES.get('key')
# 獲取加鹽cookie
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

'''
default: 預設值
salt: 加密鹽
max_age: 後臺控制過期時間
'''

cookie刪除

def logout(request):
    rep = redirect("/login/")
    rep.delete_cookie("user")  # 刪除使用者瀏覽器上之前設定的usercookie值
    return rep
# 設定cookie
def set_cookie(request):
    set_obj = HttpResponse('set cookie successfuly!')
    # set_obj.set_cookie('name', 'Hammer','nb',expires=3600)
    set_obj.set_signed_cookie('name', 'Hammer','nb')
    # set_obj.set_cookie('is_login', True)
    return set_obj

# 獲取cookie
def get_cookie(request):
    get_obj = HttpResponse('get cookie successfuly!')
    # name = request.COOKIES.get('name')
    name1 = request.get_signed_cookie('name',salt='nb')
    print(name1)
    return get_obj

# 更新cookie
def update_cookie(request):
    upd_obj = HttpResponse('update cookie successfuly!')
    name = upd_obj.set_cookie('name','Hans')
    print(name)
    return upd_obj

# 刪除cookie
def delete_cookie(request):
    del_obj = HttpResponse('delete cookie successfuly!')
    del_obj.delete_cookie('is_login')
    return del_obj

cookie示例2

'''models.py'''
class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
'''urls.py'''
from django.contrib import admin
from django.urls import path
from cookie import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login),
    path('index/', views.index),
    path('logout/', views.logout),
    path('order/', views.order)
'''views.py'''
def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    username = request.POST.get("username")
    password = request.POST.get("pwd")

    user_obj = models.UserInfo.objects.filter(username=username, password=password).first()
    print(user_obj.username)

    if not user_obj:
        return redirect("/login/")
    else:
        rep = redirect("/index/")
        rep.set_cookie("is_login", True)
        return rep
       
def index(request):
    print(request.COOKIES.get('is_login'))
    status = request.COOKIES.get('is_login') # 收到瀏覽器的再次請求,判斷瀏覽器攜帶的cookie是不是登入成功的時候響應的 cookie
    if not status:
        return redirect('/login/')
    return render(request, "index.html")


def logout(request):
    rep = redirect('/login/')
    rep.delete_cookie("is_login")
    return rep # 點選登出後執行,刪除cookie,不再儲存使用者狀態,並彈到登入頁面
   
def order(request):
    print(request.COOKIES.get('is_login'))
    status = request.COOKIES.get('is_login')
    if not status:
        return redirect('/login/')
    return render(request, "order.html")
<!--login.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 
<h3>使用者登入</h3>
<form action="" method="post">
    {% csrf_token %}
    <p>使用者名稱: <input type="text" name="username"></p>
    <p>密碼: <input type="password" name="pwd"></p>
    <input type="submit">
</form>
 
</body>
</html>
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>index頁面</h2>
 
<a href="/logout/">登出</a>
 
</body>
</html>
<!--order.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>order 頁面</h2> 
 
<a href="/logout/">登出</a>
 
</body>
</html>

總結

  • 例項化“三板斧”和JsonReponse得到的物件用法都一樣,因為他們的本質都是繼承了HttpResponse
  • 登入成功後瀏覽器儲存cookie,只要訪問該域名下的所有網頁在不過期的情況下,就一直可以攜帶或者獲取到cookie(請求頭內)
  • 需要注意的是,如果沒有設定過期時間,預設關閉瀏覽器就過期
  • 加鹽是第三個位置引數
  • cookie是儲存在客戶端的鍵值對

Django session

session的由來

Cookie雖然在一定程度上解決了“保持狀態”的需求,但是由於Cookie本身最大支援4096位元組,以及Cookie本身儲存在客戶端,可能被攔截或竊取,因此就需要有一種新的東西,它能支援更多的位元組,並且他儲存在伺服器,有較高的安全性。這就是Session。

問題來了,基於HTTP協議的無狀態特徵,伺服器根本就不知道訪問者是“誰”。那麼上述的Cookie就起到橋接的作用。

我們可以給每個客戶端的Cookie分配一個唯一的id,這樣使用者在訪問時,通過Cookie,伺服器就知道來的人是“誰”。然後我們再根據不同的Cookie的id,在伺服器上儲存一段時間的私密資料,如“賬號密碼”等等。

總結而言:Cookie彌補了HTTP無狀態的不足,讓伺服器知道來的人是“誰”;但是Cookie以文字的形式儲存在本地,自身安全性較差;所以我們就通過Cookie識別不同的使用者,對應的在Session裡儲存私密的資訊以及超過4096位元組的文字。

另外,上述所說的Cookie和Session其實是共通性的東西,不限於語言和框架

在Web開發中,使用 session 來完成會話跟蹤,session 底層依賴 Cookie 技術

session原理

伺服器在執行時可以為每一個使用者的瀏覽器建立一個其獨享的 session 物件,由於 session 為使用者瀏覽器獨享,所以使用者在訪問伺服器的 web 資源時,可以把各自的資料放在各自的 session 中,當使用者再去訪問該伺服器中的其它 web 資源時,其它 web 資源再從使用者各自的 session 中取出資料為使用者服務

img

  • a. 瀏覽器第一次請求獲取登入頁面 login。

  • b. 瀏覽器輸入賬號密碼第二次請求,若輸入正確,伺服器響應瀏覽器一個 index 頁面和一個鍵為 sessionid,值為隨機字串的 cookie,即 set_cookie ("sessionid",隨機字串)。

  • c. 伺服器內部在 django.session 表中記錄一條資料。

    django.session 表中有三個欄位。

    • session_key:存的是隨機字串,即響應給瀏覽器的 cookie 的 sessionid 鍵對應的值。
    • session_data:存的是使用者的資訊,即多個 request.session["key"]=value,且是密文。
    • expire_date:存的是該條記錄的過期時間(預設14天)
  • d. 瀏覽器第三次請求其他資源時,攜帶 cookie :{sessionid:隨機字串},伺服器從 django.session 表中根據該隨機字串取出該使用者的資料,供其使用(即儲存狀態)

注意: django.session 表中儲存的是瀏覽器的資訊,而不是每一個使用者的資訊。 因此, 同一瀏覽器多個使用者請求只儲存一條記錄(後面覆蓋前面),多個瀏覽器請求才儲存多條記錄。

cookie 彌補了 http 無狀態的不足,讓伺服器知道來的人是"誰",但是 cookie 以文字的形式儲存在瀏覽器端,安全性較差,且最大隻支援 4096 位元組,所以只通過 cookie 識別不同的使用者,然後,在對應的 session 裡儲存私密的資訊以及超過 4096 位元組的文字。

session設定

request.session["key"] = value

執行步驟:

  • a. 生成隨機字串
  • b. 把隨機字串和設定的鍵值對儲存到 django_session 表的 session_key 和 session_data 裡
  • c. 設定 cookie:set_cookie("sessionid",隨機字串) 響應給瀏覽器

示例

# 設定前先進行資料庫遷移
# 設定session
'''
1.生成隨機字串
2.把隨機字串放入cookie(sessionid,'隨機字串')
3.將資料存入到django_session表中
'''
def set_session(request):
    request.session['name'] = 'Hammer'
    request.session['is_login'] = True
    return HttpResponse('set session successfuly!')

image

ps:如果瀏覽器不刪除cookie,那麼資料庫內會一直使用這一條session記錄,直到過期

session 獲取

request.session.get('key')

執行步驟:

  • a. 從 cookie 中獲取 sessionid 鍵的值,即隨機字串。
  • b. 根據隨機字串從 django_session 表過濾出記錄。
  • c. 取出 session_data 欄位的資料。
def get_session(request):
    name = request.session.get('name')
    is_login = request.session.get('is_login')
    print(name, type(name))  # Hammer <class 'str'>
    print(is_login, type(is_login))  # True <class 'bool'>
    return HttpResponse('get session successfuly!')

session更新

def update_session(request):
    request.session['name'] = 'Hans'
    print(request.session.get('name'))  # Hans
    return HttpResponse('update session successfuly!')

執行步驟:

  • 直接去django_session表中替換,cookie還是原來的不變

session 刪除

session 刪除,刪除整條記錄(包括 session_key、session_data、expire_date 三個欄位)

request.session.flush()

刪除 session_data 裡的其中一組鍵值對:

del request.session["key"]

執行步驟:

  • a. 從 cookie 中獲取 sessionid 鍵的值,即隨機字串
  • b. 根據隨機字串從 django_session 表過濾出記錄
  • c. 刪除過濾出來的記錄
def delte_session(request):
    # del request.session['is_login']
    request.session.flush()
    return HttpResponse('del session successfuly!')

session 示例

'''urls.py'''
from session import views as session_views

urlpatterns = [
    path('session_login/', session_views.login),
    path('s_index/', session_views.s_index),
    path('s_logout/', session_views.s_logout),
]
'''views.py'''
def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    username = request.POST.get("username")
    password = request.POST.get("pwd")

    user_obj = models.UserInfo.objects.filter(username=username, password=password).first()
    print(user_obj.username)

    if not user_obj:
        return redirect("/session_login/")
    else:
        request.session['is_login'] = True
        request.session['user1'] = username
        return redirect("/s_index/")


def s_index(request):
    status = request.session.get('is_login')
    if not status:
        return redirect('/session_login/')
    return render(request, "s_index.html")


def s_logout(request):
   # del request.session["is_login"] # 刪除session_data裡的一組鍵值對
    request.session.flush() # 刪除一條記錄包括(session_key session_data expire_date)三個欄位
    return redirect('/session_login/')
<!--s_index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>session_index 頁面{{ request.session.user1 }}</h2>
<a href="/s_logout/">登出</a>
</body>
</html>

session 和中介軟體的關係

image

  • 建立的情況:瀏覽器請求沒有建立session,在響應的時候也需要經過中介軟體,這時候中介軟體幫助我們幹了三件事(生成隨機字串,寫入cookie,把資料存入資料庫),然後瀏覽器存的是sessionid
  • 獲取的情況:資料庫獲取,通過request.session
  • 更新的情況:也是在響應的時候,先校驗是否有隨機字串,有就更新,沒有就不會更新session
  • 上面的操作都在中介軟體內完成,結合上圖生命週期流程圖參考一下

session 的其他方法

# 獲取、設定、刪除Session中資料
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在則不設定
del request.session['k1']


# 所有 鍵、值、鍵值對
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()

# 會話session的key(隨機字串)
request.session.session_key

# 將所有Session失效日期小於當前日期的資料刪除
request.session.clear_expired()

# 檢查會話session的key在資料庫中是否存在
request.session.exists("session_key")

# 刪除當前會話的所有Session資料(只刪資料庫)
request.session.delete()
  
# 刪除當前的會話資料並刪除會話的Cookie(資料庫和cookie都刪)。
request.session.flush() 
    這用於確保前面的會話資料不可以再次被使用者的瀏覽器訪問

# 設定會話Session和Cookie的超時時間
request.session.set_expiry(value)
    * 如果value是個整數,session會在些秒數後失效。
    * 如果value是個datatime或timedelta,session就會在這個時間後失效。
    * 如果value是0,使用者關閉瀏覽器session就會失效。
    * 如果value是None,session會依賴全域性session失效策略。

session的其他配置(配置檔案中)

1. 資料庫Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(預設)

2. 快取Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的快取別名(預設記憶體快取,也可以是memcache),此處別名依賴快取的設定

3. 檔案Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 快取檔案路徑,如果為None,則使用tempfile模組獲取一個臨時地址tempfile.gettempdir() 

4. 快取+資料庫
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

其他公用設定項:
SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie儲存在瀏覽器上時的key,即:sessionid=隨機字串(預設)
SESSION_COOKIE_PATH = "/"                               # Session的cookie儲存的路徑(預設)
SESSION_COOKIE_DOMAIN = None                             # Session的cookie儲存的域名(預設)
SESSION_COOKIE_SECURE = False                            # 是否Https傳輸cookie(預設)
SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支援http傳輸(預設)
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2                             # Session的cookie失效日期(2周)(預設)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否關閉瀏覽器使得Session過期(預設)
SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次請求都儲存Session,預設修改之後才儲存(預設)

實際應用

img

相關文章