由於因特網HTTP協議的特性,每一次來自於用戶瀏覽器的請求(request)都是無狀態的、獨立的。通俗地說,就是沒法保存用戶狀態,後臺服務器根本就不知道當前請求和之前及之後請求是否來自同一用戶。對於靜態網站,這可能不是個問題,而對於動態網站,尤爲是京東、天貓、銀行等購物或金融網站,沒法識別用戶並保持用戶狀態是致命的,根本就沒法提供服務。你能夠嘗試將瀏覽器的cookie功能關閉,你會發現將沒法在京東登陸和購物。html
爲了實現鏈接狀態的保持功能,網站會經過用戶的瀏覽器在用戶機器內被限定的硬盤位置中寫入一些數據,也就是所謂的Cookie。經過Cookie能夠保存一些諸如用戶名、瀏覽記錄、表單記錄、登陸和註銷等各類數據。可是這種方式很是不安全,由於Cookie保存在用戶的機器上,若是Cookie被僞造、篡改或刪除,就會形成極大的安全威脅,所以,現代網站設計一般將Cookie用來保存一些不重要的內容,實際的用戶數據和狀態仍是以Session會話的方式保存在服務器端。python
可是,必須清楚的是Session依賴Cookie!不一樣的地方在於Session將全部的數據都放在服務器端,用戶瀏覽器的Cookie中只會保存一個非明文的識別信息,好比哈希值。數據庫
Django提供了一個通用的Session框架,而且可使用多種session數據的保存方式:django
一般狀況,沒有特別需求的話,請使用保存在數據庫內的方式,儘可能不要保存到Cookie內。瀏覽器
Django的session框架默認啓用,並已經註冊在app設置內,若是真的沒有啓用,那麼參考下面的內容添加有說明的那兩行,再執行migrate命令建立數據表,就可使用session了。緩存
# Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', # 這一行 'django.contrib.messages', 'django.contrib.staticfiles', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', # 這一行 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
當session啓用後,傳遞給視圖request參數的HttpRequest對象將包含一個session屬性,就像一個字典對象同樣。你能夠在Django的任何地方讀寫request.session
屬性,或者屢次編輯使用它。安全
下面是session使用參考:服務器
class backends.base.SessionBase # 這是全部會話對象的基類,包含標準的字典方法: __getitem__(key) Example: fav_color = request.session['fav_color'] __setitem__(key, value) Example: request.session['fav_color'] = 'blue' __delitem__(key) Example: del request.session['fav_color'] # 若是不存在會拋出異常 __contains__(key) Example: 'fav_color' in request.session get(key, default=None) Example: fav_color = request.session.get('fav_color', 'red') pop(key, default=__not_given) Example: fav_color = request.session.pop('fav_color', 'blue') # 相似字典數據類型的內置方法 keys() items() setdefault() clear() # 它還有下面的方法: flush() # 刪除當前的會話數據和會話cookie。常常用在用戶退出後,刪除會話。 set_test_cookie() # 設置一個測試cookie,用於探測用戶瀏覽器是否支持cookies。因爲cookie的工做機制,你只有在下次用戶請求的時候才能夠測試。 test_cookie_worked() # 返回True或者False,取決於用戶的瀏覽器是否接受測試cookie。你必須在以前先調用set_test_cookie()方法。 delete_test_cookie() # 刪除測試cookie。 set_expiry(value) # 設置cookie的有效期。能夠傳遞不一樣類型的參數值: • 若是值是一個整數,session將在對應的秒數後失效。例如request.session.set_expiry(300) 將在300秒後失效. • 若是值是一個datetime或者timedelta對象, 會話將在指定的日期失效 • 若是爲0,在用戶關閉瀏覽器後失效 • 若是爲None,則將使用全局會話失效策略 失效時間從上一次會話被修改的時刻開始計時。 get_expiry_age() # 返回多少秒後失效的秒數。對於沒有自定義失效時間的會話,這等同於SESSION_COOKIE_AGE. # 這個方法接受2個可選的關鍵字參數 • modification:會話的最後修改時間(datetime對象)。默認是當前時間。 •expiry: 會話失效信息,能夠是datetime對象,也能夠是int或None get_expiry_date() # 和上面的方法相似,只是返回的是日期 get_expire_at_browser_close() # 返回True或False,根據用戶會話是不是瀏覽器關閉後就結束。 clear_expired() # 刪除已經失效的會話數據。 cycle_key() # 建立一個新的會話祕鑰用於保持當前的會話數據。django.contrib.auth.login() 會調用這個方法。
基本上背下來上面的內容,Django的session你就能夠信手拈來了。cookie
下面結合咱們的項目實戰,使用session。session
首先,修改login/views.py
中的login()視圖函數:
def login(request): if request.session.get('is_login', None): # 不容許重複登陸 return redirect('/index/') if request.method == 'POST': login_form = forms.UserForm(request.POST) message = '請檢查填寫的內容!' if login_form.is_valid(): username = login_form.cleaned_data.get('username') password = login_form.cleaned_data.get('password') try: user = models.User.objects.get(name=username) except : message = '用戶不存在!' return render(request, 'login/login.html', locals()) if user.password == password: request.session['is_login'] = True request.session['user_id'] = user.id request.session['user_name'] = user.name return redirect('/index/') else: message = '密碼不正確!' return render(request, 'login/login.html', locals()) else: return render(request, 'login/login.html', locals()) login_form = forms.UserForm() return render(request, 'login/login.html', locals())
經過下面的if語句,咱們不容許重複登陸:
if request.session.get('is_login',None): return redirect("/index/")
經過下面的語句,咱們往session字典內寫入用戶狀態和數據:
request.session['is_login'] = True request.session['user_id'] = user.id request.session['user_name'] = user.name
你徹底能夠往裏面寫任何數據,不只僅限於用戶相關!
既然有了session記錄用戶登陸狀態,那麼就能夠完善咱們的登出視圖函數了:
def logout(request): if not request.session.get('is_login', None): # 若是原本就未登陸,也就沒有登出一說 return redirect("/login/") request.session.flush() # 或者使用下面的方法 # del request.session['is_login'] # del request.session['user_id'] # del request.session['user_name'] return redirect("/login/")
flush()方法是比較安全的一種作法,並且一次性將session中的全部內容所有清空,確保不留後患。但也有很差的地方,那就是若是你在session中夾帶了一點‘私貨’,會被一併刪除,這一點必定要注意。
有了用戶狀態,就能夠根據用戶登陸與否,展現不一樣的頁面,好比在index頁面中顯示當前用戶的名稱:
修改index.html的代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首頁</title> </head> <body> <h1>{{ request.session.user_name }}! 歡迎回來!</h1> <p> <a href="/logout/">登出</a> </p> </body> </html>
注意其中的模板語言,{{ request }}
這個變量會被默認傳入模板中,能夠經過圓點的調用方式,獲取它內部的{{ request.session }}
,再進一步的獲取session中的內容。其實{{ request }}
中的數據遠不止此,例如{{ request.path }}
就能夠獲取先前的url地址。
從新啓動服務器,進行登陸和登出測試:
能夠看出,在已經login的狀態下,手動從瀏覽器地址欄中訪問/login/也依然進入的是index頁面。在logout的狀態下,都會跳轉到login頁面。可是,須要注意的是,咱們目前尚未編寫index未登陸限制訪問的代碼。
修改index視圖函數,添加相關限制:
def index(request): if not request.session.get('is_login', None): return redirect('/login/') return render(request, 'login/index.html')