Cookie與Session
1. Cookie
1.1 會話追蹤
一次客戶端與服務端之間的會話可能會包含多次請求和響應,而在一個會話的多個請求之間共享資料,這就叫會話跟蹤技術。在web中會話跟蹤的實現要依靠cookie和session。
1.2 cookie的由來
1)保持狀態
HTTP協議是無狀態的,每一次的請求都沒有直接關係,對伺服器來說,每一次的請求都是全新的。
而我們需要保持每次一會話產生的資料(保持狀態),所以就用到了cookie技術。
2)cookie為何物
cookie是伺服器傳送出來儲存在瀏覽器上的一組組鍵值對,下一次訪問伺服器時瀏覽器會自動攜帶這些鍵值對,以讓伺服器提取資訊。
可以用Ctrl + Shift + del三個鍵來清除頁面快取和cookie。
1.3 cookie的原理
1)圖示
- 瀏覽器訪問服務端時,帶著一個空的cookie,然後由伺服器產生內容,瀏覽器收到響應後儲存在本地;
- 當瀏覽器再次訪問時,會自動帶上cookie,這樣伺服器就能通過cookie的內容來判斷出客戶端身份了。
2)cookie的規範
- cookie大小上限為4KB;
- 一個伺服器最多在客戶端瀏覽器上儲存20個Cookie;
- 一個瀏覽器最多儲存300個Cookie;
上面是HTTP的Cookie規範,並非絕對法則,各個瀏覽器可能會對Cookie進行擴充套件。
不同的瀏覽器之間是不共享Cookie的。
3)cookie與HTTP頭
Cookie是通過HTTP請求和響應頭在客戶端和伺服器端傳遞的。
- 請求頭(客戶端傳送給服務端)Cookie:a=xxxx; b=ooo; c=yyyy
- 多個Cookie用分號隔開
- 響應頭(服務端傳送給客戶端)Set-Cookie:a=xxx Set-Cookie:b=ooo Set-Cookie:c=yyy
- 一個Cookie物件用一個Set-Cookie
4)cookie的覆蓋
- 如果服務端傳送重複的Cookie,那麼會覆蓋原有的Cookie。
2. Django中操作Cookie
2.1 獲取Cookie
request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
引數:
- default:預設值
- salt:加密鹽
- max_age:後臺控制過期時間
2.2 設定Cookie
rep = HttpResponse(...) rep = render(request, ...) rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密鹽', max_age=None, ...)
引數:
- key:鍵
- value:值
- max_age=None
- 超時時間,單位是秒,預設時長是2周
- None表示這個cookie會延續到瀏覽器關閉為止
- expires=None
- 超時時間(IE requires expires, so set it if hasn't been already.)
- 值是一個datatime型別的時間日期物件,到這個日期就失效的意思
- path='/'
- Cookie生效的路徑
- 瀏覽器只會把cookie回傳給帶有該路徑的頁面,這樣可以避免將cookie傳給站點中的其他應用
- / 根路徑的cookie可以被任何url的頁面訪問
- domain=None
- Cookie的生效域名,如果設定為None,cookie就只能由設定它的站點讀取
- 可以用這個引數來構造一個跨站Cookie,如 domain=".example.com"所構造的cookie對它的子域站點都是可讀的
- secure=False
- 是否使用https來回傳cookie
- httponly=False
- 只能http協議傳輸,無法被JavaScript獲取(可以抓包獲取)
注意:
- Cookie在設定時不允許出現中文
- 如果非要設定中文,可以對中文 .encode('utf-8').decode('iso-8859-1')或者直接 json.dumps('傻X')
2.3 刪除Cookie
def logout(request): rep = redirect("/login/") rep.delete_cookie("user") # 刪除使用者瀏覽器上之前設定的usercookie值 return rep
2.4 Cookie登入校驗示例
def check_login(func): @wraps(func) def inner(request, *args, **kwargs): next_url = request.get_full_path() if request.get_signed_cookie("login", salt="SSS", default=None) == "yes": # 已經登入的使用者... return func(request, *args, **kwargs) else: # 沒有登入的使用者,跳轉剛到登入頁面 return redirect("/login/?next={}".format(next_url)) return inner def login(request): if request.method == "POST": username = request.POST.get("username") passwd = request.POST.get("password") if username == "xxx" and passwd == "dashabi": next_url = request.GET.get("next") if next_url and next_url != "/logout/": response = redirect(next_url) else: response = redirect("/class_list/") response.set_signed_cookie("login", "yes", salt="SSS") return response return render(request, "login.html")
3. Session
3.1 由來
Cookie雖然在一定程度上解決了“保持狀態”的需求,但是由於Cookie本身最大隻支援4096位元組,且Cookie本身儲存在客戶端,可能被攔截或竊取。因此就需要一種新的技術,它能支援更多的位元組,並且它儲存在伺服器端,有較高的安全性。
3.2 Session為何物
Session是服務端的技術,伺服器在執行時為每一個使用者的瀏覽器建立一個其獨享的session物件。
由於session為使用者瀏覽器獨享,所以使用者在訪問伺服器的web資源時,可以把各自的資料放在各自的session中。當用戶再去訪問該伺服器中的其他web資源時,其他web資源再從使用者各自的session中取出資料。
3.3 Session的原理
給每一個Cookie分配一個唯一的id,使用者訪問時,通過Cookie,伺服器就知道來將的身份。然後再根據不同的Cookie的id,在伺服器上儲存一段時間的私密資料(賬號密碼等)。這時Cookie就起到了橋接的作用。
由此,我們通過Cookie來識別不同的使用者,然後在對應的session中儲存私密的資訊。
4. Django中操作session
4.1 Django中Session的方法
# 獲取、設定、刪除Session中資料#取值 # 獲取值 request.session['k1'] request.session.get('k1',None) # request.session這句是從cookie裡面將sessionid的值取出來 # 將django-session表裡面的對應sessionid的值的那條記錄中的session-data欄位的資料拿出來(並解密) # get方法就取出k1這個鍵對應的值 # 設定值 request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在則不設定 # 生成隨機字串,將這個隨機字串和使用者資料(加密後)和過期時間儲存到了django-session表裡面 # 將這個隨機字串以sessionid:隨機字串的形式新增到cookie裡面返回給瀏覽器,這個sessionid名字是可以改的 # 注意:django-session表不能通過orm來直接控制,因為models.py裡面沒有這個對應關係 #刪除值 del request.session['k1'] #django-session表裡面同步刪除 # 所有 鍵、值、鍵值對 request.session.keys() request.session.values() request.session.items() # 會話session的key session_key = request.session.session_key # 獲取sessionid的值 # 將所有session失效日期小於當前日期的資料刪除,將過期的刪除 request.session.clear_expired() # 檢查會話session的key在資料庫中是否存在 request.session.exists("session_key") #session_key就是那個sessionid的值 # 刪除當前會話的所有Session資料 request.session.delete() # 刪除當前的會話資料並刪除會話的Cookie。 request.session.flush() # 常用,清空所有cookie---刪除session表裡的這個會話的記錄, # 這用於確保前面的會話資料不可以再次被使用者的瀏覽器訪問 # 例如,django.contrib.auth.logout() 函式中就會呼叫它 # 設定會話Session和Cookie的超時時間 request.session.set_expiry(value) # 如果value是個整數,session會在些秒數後失效 # 如果value是個datatime或timedelta,session就會在這個時間後失效 # 如果value是0,使用者關閉瀏覽器session就會失效 # 如果value是None,session會依賴全域性session失效策略
4.2 Session詳細流程解析
4.3 Session版的登入驗證
from functools import wraps def check_login(func): @wraps(func) def inner(request, *args, **kwargs): next_url = request.get_full_path() if request.session.get("user"): return func(request, *args, **kwargs) else: return redirect("/login/?next={}".format(next_url)) return inner def login(request): if request.method == "POST": user = request.POST.get("user") pwd = request.POST.get("pwd") if user == "alex" and pwd == "alex1234": # 設定session request.session["user"] = user # 獲取跳到登陸頁面之前的URL next_url = request.GET.get("next") # 如果有,就跳轉回登陸之前的URL if next_url: return redirect(next_url) # 否則預設跳轉到index頁面 else: return redirect("/index/") return render(request, "login.html") @check_login def logout(request): # 刪除所有當前請求相關的session request.session.delete() return redirect("/login/") @check_login def index(request): current_user = request.session.get("user", None) return render(request, "index.html", {"user": current_user})
注意:
- 在同一個瀏覽器上,第一個使用者已經登入,當另一個使用者也登入時,會帶著第一個使用者的session_id,但是其他內容都將會覆蓋
- 一個網站對一個瀏覽器,是一個sessionid,換一個瀏覽器,肯定會生成另外一個sessionid
4.4 Django中Session配置
- Django中預設支援Session,它內部提供了5中型別的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 = 1209600 # Session的cookie失效日期(2周)(預設) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否關閉瀏覽器使得Session過期(預設) SESSION_SAVE_EVERY_REQUEST = False # 是否每次請求都儲存Session,預設修改之後才儲存(預設)