Django Cookie與Session
Django cookie
cookie由來
以前的網站都是靜態的,早期的論壇,新聞網頁都是不需要登入的,但是隨著發展,動態網站的誕生需要登入使用,我們知道HTTP協議的四大特性之一是無狀態,就是每次請求都是獨立的,它的執行情況和結果與前面的請求和之後的請求都無直接關係,它不會受前面的請求響應情況直接影響,也不會直接影響後面的請求響應情況;
狀態可以理解為客戶端和伺服器在某次會話中產生的資料,那無狀態的就以為這些資料不會被保留。會話中產生的資料又是我們需要儲存的,也就是說要“保持狀態”。因此Cookie就是在這樣一個場景下誕生
cookie介紹
Cookie的結構是key-value結構,類似於python中的字典,隨著伺服器端的響應傳送給客戶端瀏覽器。然後客戶端瀏覽器會把Cookie儲存起來,當下一次再訪問伺服器時把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,例如客戶端的第一個請求伺服器端傳送的Cookie是:Set-Cookie: a=A;第二請求伺服器端傳送的是:Set-Cookie: a=AA,那麼客戶端只留下一個Cookie,即:a=AA;
cookie 檢視
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獲取(不是絕對,底層抓包可以獲取到也可以被覆蓋)
cookie 設定
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 示例1
# 設定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 中取出資料為使用者服務
-
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!')
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 和中介軟體的關係
- 建立的情況:瀏覽器請求沒有建立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,預設修改之後才儲存(預設)