一、Cookie和Session
首先、HTTP協議是無狀態的;所謂的無狀態是指每次的請求都是獨立的,它的執行情況和結果與前面的請求和之后的請求都無直接關系,它不會受前面的請求響應直接影響,也不會直接影響后面的請求響應情況。簡單來說就是,對服務器來說,每次的請求都是全新的。
狀態可以理解為客戶端和服務器在某次會話中產生的數據,那無狀態的就以為這些數據不會被保留。會話中產生的數據又是我們需要保存的,也就是說要“保持狀態”。因此Cookie就是在這樣一個場景下誕生。
cookie雖然在一定程度上解決了“保持狀態”的需求,但是由於cookie本身最大支持4096字節,以及cookie本身保存在客戶端,可能被攔截或竊取,因此就需要有一種新的東西,它能支持更多的字節,並且他保存在服務器,有較高的安全性。這就是session。
問題來了,基於http協議的無狀態特征,服務器根本就不知道訪問者是“誰”。那么上述的cookie就起到橋接的作用。
我們可以給每個客戶端的cookie分配一個唯一的id,這樣用戶在訪問時,通過cookie,服務器就知道來的人是“誰”。然后我們再根據不同的cookie的id,在服務器上保存一段時間的私密資料,如“賬號密碼”等等。
總結而言:cookie彌補了http無狀態的不足,讓服務器知道來的人是“誰”;但是cookie以文本的形式保存在本地,自身安全性較差;所以我們就通過cookie識別不同的用戶,對應的在session里保存私密的信息以及超過4096字節的文本。
另外,上述所說的cookie和session其實是共通性的東西,不限於語言和框架。
二、用戶登陸應用的原理
用戶從登陸到登陸成功后的其他展示操作頁面是怎么聯系的,也就是說,是怎么確保用戶只有登陸驗證成功后才能打開其他展示操作頁面的。實際測試的情況是,如果繞過登陸頁面,直接輸入后台的url地址也可以直接訪問其他頁面,這樣顯然是不合理的,這就需要cookie和session配合驗證。有了這個驗證過程,我們就可以實現和其他網站一樣只有登陸驗證成功后才能進入其他后台頁面。
這種認證機制的過程。每當我們使用瀏覽器訪問一個登陸頁面的時候,一旦我們通過了認證。服務器端就會發送一組隨唯一的字符串(比如是 abcdef)到瀏覽器端並暫時儲存起來,這個被儲存在瀏覽器的東西就叫cookie,而服務器端也會自己存儲一下用戶當前的狀態,比如 login = True,username = beibei 之類的用戶信息。但是這種存儲是以字典形式存儲的,字典的唯一key就是剛才發給用戶的唯一的cookie值。那么如果在服務器端查看session信息的話,理論上就會看到如下的字典
{abcdef:{'login':True,'username:beibei}}
因為每個cookie都是唯一的,所以我們在電腦上換個瀏覽器在登陸同一個網站也需要再次驗證。那么為什么說我們只是理論上看到這樣子的字典呢?因為出於安全性的考慮,其實對於上面那個大字典,不只是key值abcdef是被加密的,value值{'login':True,'username:beibei}在服務器端也是一樣被加密的。所以我們在服務器上就算打開session信息看到的也是一串被加密過的字符串。
Cookie的定義
cookie翻譯成中文意思是曲奇餅、餅干。然而,在因特網內cookie就和餅干沒關系了。Cookie具體指的是一段小信息,它是服務器發送出來存儲在瀏覽器上的一組組鍵值對,下次訪問服務器時會自動攜帶這些鍵值對,以便服務器提取有用信息。
查看Cookie
使用Chrome瀏覽器,打開開發者工具。

三、Django中操作Cookie
獲取Cookie
request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
參數說明:
- default: 默認值
- salt:加密鹽
- max_age:后台控制過期時間
設置Cookie
rep = HttpResponse(...) rep = render(request, ...) rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密鹽',...)
參數:
- key, 鍵
- value='', 值
- max_age=None, 超時時間
- expires=None, 超時時間(IE requires expires, so set it if hasn't been already.)
- path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie可以被任何url的頁面訪問
- domain=None, Cookie生效的域名
- secure=False, https傳輸
- httponly=False 只能http協議傳輸,無法被JavaScript獲取(不是絕對,底層抓包可以獲取到也可以被覆蓋)
刪除Cookie
def logout(request): rep = redirect("/login/") rep.delete_cookie("user") # 刪除用戶瀏覽器上之前設置的usercookie值 return rep
登錄認證示例:
需要知道幾點
一共有三次請求
注意:form表單的action走的路徑還是/login/
第一次請求:url:http://127.0.0.1:8080/login get請求
第一次請求:url:http://127.0.0.1:8080/login post請求 user pasw
第一次請求:url:http://127.0.0.1:8080/main post請求 攜帶着cookie的了
所以在main頁面中就會取到cookie,因為這是的main里面已經有cookie了
Cookie版登陸校驗:只需要在views.py中的后台函數上加一個驗證裝飾器
def check_login(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("/main/") response.set_signed_cookie("login", "yes", salt="SSS") return response return render(request, "login.html")
Django中Session相關用法
def index(request): # 獲取、設置、刪除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的隨機字符串 request.session.session_key # 將所有Session失效日期小於當前日期的數據刪除 request.session.clear_expired() # 檢查 用戶session的隨機字符串 在數據庫中是否 request.session.exists("session_key") # 刪除當前用戶的所有Session數據 request.session.delete() request.session.set_expiry(value) * 如果value是個整數,session會在些秒數后失效。 * 如果value是個datatime或timedelta,session就會在這個時間后失效。 * 如果value是0,用戶關閉瀏覽器session就會失效。 * 如果value是None,session會依賴全局session失效策略。
Session版登陸驗證
# 自定義session登陸驗證裝飾器 def check_login(func): def inner(request, *args, **kwargs): # 把當前訪問的網址拿到 url = request.get_full_path() login_status = request.session.get('login') login_user = request.session.get('user') if login_status == '1' and login_user == 'beibei': return func(request, *args, **kwargs) else: return redirect('/login/?next={}'.format(url)) return inner # 登錄函數 def login(request): if request.method == 'POST': input_username = request.POST.get('username') input_password = request.POST.get('password') information_obj = Infomation.objects.all() if input_username: if input_password: for i in range(len(information_obj)): username = Infomation.objects.get(id=i + 1).username password = Infomation.objects.get(id=i + 1).password if input_username == username and input_password == password: # session驗證 request.session['user'] = 'beibei' request.session['login'] = '1' next_url = request.GET.get('next') request.session.set_expiry(3600) # 1個小時后失效 if next_url: return redirect(next_url) else: return redirect(reverse('main')) else: pro = '用戶名和密碼不正確' return render(request, "登陸頁面.html", {"pro": pro}) else: pass_pro = '密碼不能為空' return render(request, "登陸頁面.html", {"pass_pro": pass_pro}) else: user_pro = '用戶名不能為空' return render(request, "登陸頁面.html", {"user_pro": user_pro}) return render(request, "登陸頁面.html") # 注銷(退出登錄)函數 @check_login def logout(request): request.session.delete() return redirect(reverse('login')) @check_login def main(request): return render(request, '主頁面.html')
Django中的Seesion配置
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,默認修改之后才保存(默認)
PS:
在Django中Session是通過一個中間件管理的。如果要在應用程序中使用Session,需要在settings.py中的MIDDLEWARE_CLASSES變量中加入’django.contrib.sessions.middleware.SessionMiddleware’。 Django中的Session有3種存儲方式:放在數據庫、緩存或者文件系統中,下面分別予以介紹。 1. 將Session存儲在數據庫中: 如果要將Session存儲在數據庫中,我們需要將 ’django.contrib.sessions’ 加入到INSTALLED_APPS 變量中。然后運行 manage.py syncdb 在數據庫中創建相應的存儲Session的數據庫表。 2. 將Session存儲在緩存中: 如果想獲得更好的性能,我們可以將Session保存在緩存中。這里有兩種配置方式:一種是設置SESSION_ENGINE 為”django.contrib.sessions.backends.cache” 。這是一種簡單配置,Session將之被保存在緩存中,但是不保證Session總是能取到(比如緩存溢出時Session會丟失);另一種方式是設置SESSION_ENGINE 為 “django.contrib.sessions.backends.cached_db”。這種方式下,Session在保存到緩存的同時還會被保存到數據庫中,當Django在緩存中找不到Session時,會從數據庫中找到。第二種方式會有一點點性能開銷,但是安全性和冗余性更好。 3. 將Session存儲在文件系統中: 最后一種方式是將Session存儲在文件系統中。需要設置SESSION_ENGINE 為”django.contrib.sessions.backends.file”,這時你還需要同時設置SESSION_FILE_PATH 變量,它代表Session文件保存的位置,缺省的設置一般是tempfile.gettempdir(),表示系統的臨時目錄。這里要確保應用程序對那個目錄有讀寫的權限。
CBV中加載裝飾器相關
CBV實現的登錄視圖
class LoginView(View): def get(self, request): """ 處理GET請求 """ return render(request, 'login.html') def post(self, request): """ 處理POST請求 """ user = request.POST.get('user') pwd = request.POST.get('pwd') if user == 'alex' and pwd == "alex1234": next_url = request.GET.get("next") # 生成隨機字符串 # 寫瀏覽器cookie -> session_id: 隨機字符串 # 寫到服務端session: # { # "隨機字符串": {'user':'alex'} # } request.session['user'] = user if next_url: return redirect(next_url) else: return redirect('/index/') return render(request, 'login.html')
要在CBV視圖中使用我們上面的check_login裝飾器,有以下三種方式:
from django.utils.decorators import method_decorator
1. 加在CBV視圖的get或post方法上
from django.utils.decorators import method_decorator class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") @method_decorator(check_login) def post(self, request): print("Home View POST method...") return redirect("/index/")
2. 加在dispatch方法上
from django.utils.decorators import method_decorator class HomeView(View): @method_decorator(check_login) def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
因為CBV中首先執行的就是dispatch方法,所以這么寫相當於給get和post方法都加上了登錄校驗。
3. 直接加在視圖類上,但method_decorator必須傳 name 關鍵字參數
如果get方法和post方法都需要登錄校驗的話就寫兩個裝飾器。
from django.utils.decorators import method_decorator @method_decorator(check_login, name="get") @method_decorator(check_login, name="post") class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
中間件CSRF Token
CSRF Token相關裝飾器在CBV只能加到dispatch方法上
備注:
- csrf_protect,為當前函數強制設置防跨站請求偽造功能,即便settings中沒有設置全局中間件。
- csrf_exempt,取消當前函數防跨站請求偽造功能,即便settings中設置了全局中間件。
from django.views.decorators.csrf import csrf_exempt, csrf_protect class HomeView(View): @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
另外:
Session其實就是會話變量的保存地,只要是能使用變量的地方,都能使用 Sesion 變量。比如可以用來 計數、存儲臨時信息、甚至還可以存儲DataTable,只要你的服務器的內存足夠大就行。
要真正知道訪怎么用它,需要理解兩點:
1、Session 變量是存在服務器內存中的,應盡量少用,否則服務器會累着的
2、Session 就是變量的泛型集合,所有使用變量的地方理論上都可以使用 Session 變量來實現。
