咱們知道HTTP協議的四大特性中有一特性:無狀態python
無狀態:服務器沒法保存客戶端瀏覽器的任何信息,沒法記住每次訪問服務器的客戶端瀏覽器是誰。也就是說客戶端瀏覽器每次對服務器的訪問請求都是獨立的,它的知心話狀況和結果與前面任何依次的請求和以後的全部請求都是沒有直接關係的。它不會受前面的請求響應狀況直接影響,也不會直接影響後面的請求響應狀況。數據庫
對服務器來講:客戶端瀏覽器每次的訪問請求都是新的請求,服務端無論該瀏覽器是否以前已經訪問過我,你只要訪問我,我就給你響應django
狀態:能夠理解爲客戶端和服務器在某次會話中產生的數據,那無狀態的就是這些數據不會被保留。會話中產生的數據又是咱們須要保存的,也就是說要「保持狀態」。所以Cookie就是在這樣一個場景下誕生。瀏覽器
Cookie:具體指的就是一小段信息,他是服務器發送出來存處在瀏覽器上的一組鍵值對,當該瀏覽器下次訪問服務器時,瀏覽器會自動攜帶這些記錄在瀏覽器上的鍵值對,用於服務器從訪問的瀏覽器身上提取有用的信息緩存
注意:Cookies既然時瀏覽器訪問服務器時,服務器發送給個人,那我瀏覽器固然也拒絕保存服務器給我發送的這組信息(鍵值對)安全
Cookie工做的原理:在客戶端瀏覽器訪問服務器時,有服務器產生的一部分信息,這個信息就是一個鍵值對(Cookie),瀏覽器接收服務端發送的信息(Cookie)鍵值對後,保存在瀏覽器本身身上;當瀏覽器再次訪問該服務器時,瀏覽器會自動帶上(Cookie)鍵值對,這樣服務器就能經過獲取瀏覽器請求攜帶的參數(Cookie鍵值對)的內容來判斷這個是「哪個瀏覽器」了。服務器
這裏咱們先去訪問本身的博客園,使用後谷歌瀏覽器,F12 打開開發者工具,在Network那一欄中有的谷歌瀏覽器會顯示Cookie,這裏咱們用Application,就能夠看到在當前瀏覽器訪問博客園的服務器時,瀏覽器本身自己記錄的Cookie(鍵值對)cookie
此處再次提出咱們的Django響應數據的三種方式:session
return HttpResponse() # 直接返回字符串渲染頁面 return render()# 返回html頁面 return redirect()# 重定向
上述:還記得,上面的三種方式實際上是一個個的類把,當類加()的時候,實例化一個對象出來,因此我寫成下面這樣的方式再響應返回數據也能夠把
obj1 = Httprespose() return obj obj2 = render() return obj obj3 = redirect() return obj # 向上面的方式,經過這三個類先實例化一個對象,而後再返回這個對象 # 那咱們是否是又能聯想到,既然是對象,我是否是就能夠對這個對象賦值屬性或者是方法啊
obj = redirect('/login') obj.set_cookie(key,value) # 由於瀏覽器是以鍵值對的方式去存儲cookie的 ##############參數 key 鍵 value 值 max_age=None cookie的有效時間,秒爲單位
obj = redirect('/home') # 先生成一個跳轉到home頁面的對象 request.COOKIES.get(key): # ()裏面的參數是服務端給瀏覽器返回的鍵值對的key
obj = redirect("/login/") obj.delete_cookie(key) return obj
咱們這裏以一個登陸案例爲例:
有四個頁面:登陸用戶、主頁面home 、index.頁面,退出登陸頁面logout
需求,當用戶未登陸時,不能訪問home、index、logout三個頁面;當用戶登陸成功之後,瀏覽器在本地保存服務器給本身發送的Cookie值,並能夠去訪問home頁面、index頁面、logout頁面、logout頁面是當用戶登陸成功之後,清除用戶瀏覽器保存在本地的Cookie值的
# 登陸裝飾器 from functools import wraps def login_auth(func): @wraps(func) # 這裏把每個被裝飾功能函數的request接收 def inner(request,*args,**kwargs): # 經過request.COOKIES.get('name') 去哪這個瀏覽器本地有沒有Cookie值 if request.COOKIES.get('name'): res = func(request,*args, **kwargs) return res else: ''' 這裏補充兩個知識點: 1. request.path_info 只拿該url,不拿get請求攜帶的參數 2. request.get_full_path() url的全路徑,包括url ''' # 若是瀏覽器中本地沒有保存cookie值,那就把當前url的路徑拿到, last_page = request.path_info # 拿到路徑後經過get請求攜帶參數的方式重定向到登陸頁面 return redirect('/login/?next=%s' %last_page) return inner
# login視圖函數 def login(request): # 判斷login登陸頁面是否爲POST請求 if request.method == 'POST': # 拿到用戶的登陸用戶名,密碼 username = request.POST.get('username') password = request.POST.get('password') # 這裏我就拿固定的值去校驗,這裏以後寫項目的時候能夠從數據庫中拿值作檢驗 if username == 'cecilia' and password == '123': # 若是用戶名,密碼都校驗正確之後,在拿到經過裝飾器裝飾的頁面的url # 這裏再裝飾器的時候,就已是經過get請求攜帶參數的形式跳轉到login登陸頁面的 next_url = request.GET.get('next') # 因此這裏是登陸以前的頁面的url obj = redirect(next_url) # 將redirect直接賦給一個對象 if next_url == '/logout/': # 判斷這個上一次的url是否是退出登陸的url, # 若是是的話,實例化一個跳轉到本身頁面的對象 obj = redirect('/logout') # 若是不是,就登陸以後設置cookie值obj.set_cookie('name','cecilia') obj.set_cookie('name','cecilia') return obj return render(request,'login.html')
@login_auth # 首頁 def home(request): return HttpResponse('我是主頁,home主頁!') @login_auth # index頁 def index(request): return HttpResponse('我是index頁面') # 清除cookie @login_auth # 退出登陸 def logout(request): obj = redirect('/login/') # 清除瀏覽器本地的cookie obj.delete_cookie('name') return obj
# login.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post"> <p>用戶名:<input type="text" name="username"></p> <p>密碼:<input type="text" name="password"></p> <p><input type="submit"></p> </form> </body> </html>
Cookie雖然在必定程度上解決了「保持狀態」的需求,可是因爲Cookie自己最大支持4096字節,以及Cookie自己保存在客戶端,可能被攔截或竊取,所以就須要有一種新的東西,它能支持更多的字節,而且他保存在服務器,有較高的安全性。這就是Session。
問題來了,基於HTTP協議的無狀態特徵,服務器根本就不知道訪問者是「誰」。那麼上述的Cookie就起到橋接的做用。
咱們能夠給每一個客戶端的Cookie分配一個惟一的id,這樣用戶在訪問時,經過Cookie,服務器就知道來的人是「誰」。而後咱們再根據不一樣的Cookie的id,在服務器上保存一段時間的私密資料,如「帳號密碼」等等。
總結而言:Cookie彌補了HTTP無狀態的不足,讓服務器知道來的人是「誰」;可是Cookie以文本的形式保存在本地,自身安全性較差;因此咱們就經過Cookie識別不一樣的用戶,對應的在Session裏保存私密的信息以及超過4096字節的文本。
另外,上述所說的Cookie和Session實際上是共通性的東西,不限於語言和框架。
request.session[key]=value
在服務端設置session的時候,會發生下面這幾件事情:
在瀏覽器請求服務端的時候,django內部自動生成了隨機加密字符串
在django_session表中存入數據
提示1:在服務端添加session值以前,必需要先將django須要用到的表用數據庫的遷移命令遷移
提示2:這個django_session表中有三個字段
session_key : key session_data:value exprie_date:session的過時時間,django默認的是14天
將產生的隨機字符串返回給瀏覽器,讓瀏覽器在本地也保存一份
# 設置session以後, request.session.set_expiry(value) * 若是value是個整數,session會在些秒數後失效。 * 若是value是個datatime或timedelta,session就會在這個時間後失效。 * 若是value是0,用戶關閉瀏覽器session就會失效。 * 若是value是None,session會依賴全局session失效策略。14天
request.session.get(key)
在獲取瀏覽器的session時,會發生下面幾件事情:
# 第一種: request.session.delete() # 只會刪除在服務端的session,就是把django_session表中對應的記錄給刪了 # 第二種: request.session.flush() # 服務器和瀏覽器保存的都刪掉
# 獲取、設置、刪除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")
咱們這裏以一個登陸案例爲例:
有四個頁面:登陸用戶、主頁面s_home 、s_index.頁面,刪除session頁面del_session
需求,當用戶未登陸時,不能訪問s_home、s_index、del_session三個頁面;當用戶登陸成功之後,瀏覽器在本地保存服務器給本身發送的Cookie值,並能夠去訪問home頁面、s_index頁面、del_session頁面、del_session頁面是當用戶登陸成功之後,刪除在服務端的session值的
# 登陸裝飾器 def s_login_auth(func): @wraps(func) def inner(request,*args,**kwargs): # 獲取當前被裝飾的函數的url last_page = request.get_full_path() if request.session.get('name'): res = func(request,*args,**kwargs) return res else: # 若是客戶端沒有session,那帶着本身的url到登陸頁面 return redirect('/s_login/?next=%s'%last_page) return inner
# 登陸視圖函數 def s_login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 查看用戶輸入的用戶名和密碼是否正確 if username == 'cecilia' and password == '123': # 正確之後,登陸成功,在復渠段記錄session值 request.session['name'] = username # 獲取從裝飾器傳過來的上一個頁面的url last_page = request.GET.get('next') # 判斷上一個頁面的url是否存在 if last_page: # 存在則直接重定向上一個頁面,此時是已經保存session值的 return redirect(last_page) else: # 不存在,就默認回到首頁 return redirect('/s_home') return render(request, 'login.html')
@s_login_auth # 首頁 def s_home(request): return HttpResponse('我是主頁,是home頁面') @s_login_auth # s_index頁面 def s_index(request): return HttpResponse('我是index頁面') @s_login_auth # 刪除session頁面,此處就默認是刪除服務端的session def del_session(request): request.session.delete() # request.session.flush() 將服務端和瀏覽器的session值全都刪掉 return HttpResponse('刪除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,默認修改以後才保存(默認)
就以上面的設置服務端的session爲例,咱們用CBV來寫s_login的get請求和post請求的功能
咱們要給CBV加裝飾器的話,首先要導入from django.utils.decorators import method_decorator
# 裝飾器 from functools import wraps def s_login_auth(func): @wraps(func) def inner(request,*args,**kwargs): last_page = request.get_full_path() if request.session.get('name'): res = func(request,*args,**kwargs) return res else: return redirect('/c_login/?next=%s'%last_page) return inner # CBV實現登陸頁面 from django.views import View class LoginView(View): # get請求時 def get(self, request): return render(request, 'login.html') # post請求時 def post(self, request): username = request.POST.get('username') password = request.POST.get('password') if username == 'cecilia' and password == '123': request.session['name'] = username last_page = request.GET.get('next') if last_page: return redirect(last_page) else: return redirect('/c_home') else: return redirect('/c_login') ##############################################################開始加裝飾器 # 首先導入下面的 from django.utils.decorators import method_decorator # home頁面 @method_decorator(s_login_auth,name='get') # 給這個類的get的方法加裝飾器 class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return HttpResponse('我是主頁,是home頁面') # index頁面 @method_decorator(s_login_auth,name='get')# 給這個類的get的方法加裝飾器 class IndexView(View): def dispatch(self, request, *args, **kwargs): return super(IndexView, self).dispatch(request, *args, **kwargs) def get(self, request): return HttpResponse('我是index頁面') # 刪除session頁面 @method_decorator(s_login_auth,name='get')# 給這個類的get的方法加裝飾器 class DelView(View): def dispatch(self, request, *args, **kwargs): return super(DelView, self).dispatch(request, *args, **kwargs) def get(self, request): request.session.flush()# 刪除服務端和瀏覽器的session return redirect('/c_login')
1. 直接加載CBV視圖類上:但method_decorator必須傳 name 關鍵字參數
注意: 這個裝飾器要@method_decorator(裝飾器的名字,name=’被裝飾的方法‘)
# 首先導入下面的 from django.utils.decorators import method_decorator # home頁面 @method_decorator(s_login_auth,name='get') # 給這個類的get方法加裝飾器,get方法會使用裝飾器,而post方法則不會 class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return HttpResponse('我是主頁,是home頁面') def post(self,request): pass
2. 加在dispatc和方法上
注意:這個裝飾器要@method_decorator(裝飾器的名字),而且這個CBV裏的get和post方法就都使用了裝飾器
由於CBV中首先執行的就是dispatch方法,因此這麼寫至關於給get和post方法都加上了登陸校驗。
from django.utils.decorators import method_decorator # home頁面 class HomeView(View): @method_decorator(s_login_auth) def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return HttpResponse('我是主頁,是home頁面') def post(self,request): pass
3. 直接加上視圖的get或post方法上
from django.utils.decorators import method_decorator # home頁面 class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) @method_decorator(s_login_auth) def get(self, request): return HttpResponse('我是主頁,是home頁面') @method_decorator(s_login_auth) def post(self,request): pass