一,前言
1.1,什么是會話跟蹤技術
在JavaWeb中,客戶向某一服務器發出第一個請求開始,會話就開始了,直到客戶關閉了瀏覽器會話結束。在一個會話的多個請求中共享數據,這就是會話跟蹤技術。
例如在一個會話中的請求如下(請求銀行主頁):
- 請求登錄(請求參數是用戶名和密碼)
- 請求轉賬(請求參數與轉賬相關的數據)
- 請求信譽卡還款(請求參數與還款相關的數據)
在上面會話中,當前用戶信息必須在這個會話中共享的,因為登錄的是張三,那么在轉賬和還款時一定是相對張三的轉賬和還款!這就說明我們必須在一個會話過程中有共享數據的能力。
在程序中,會話跟蹤是很重要的事情。理論上,一個用戶的所有請求都應該屬於同一個會話,而另一個用戶的所有請求則該屬於另一個會話,二者不能混淆。例如,用戶A在超市購買的任何商品都應該放在A的購物車內,不論是用戶A什么時候購買的,這都是屬於同一個會話,不能放在用戶B或者用戶C的購物車內,這不屬於同一個會話。
1.2,會話路徑技術使用Cookie 或 session 完成
我們知道HTTP協議是無狀態協議,也就是說每個請求都會獨立的! 無法記錄前一次請求的狀態。但是HTTP協議中可以使用Cookie來完成會話跟蹤技術!
在JavaWeb開發中,使用session來完成會話跟蹤,session底層依賴Cookie技術。
- cookie就是一段字符串,保存於本機電腦上
- session保存於服務器,用來保存用戶的會話信息,依賴於Cookie
cookie是通過在客戶端記錄信息確定用戶身份,Session是通過在服務端記錄信息確定用戶身份。
1.3,cookie和session機制
cookie機制采用的時在客戶端保存狀態的方案。作用就是為了解決HTTP協議無狀態的缺陷所作的努力。
session機制采用的是一種在客戶端與服務端之間保持狀態的解決方案。由於采用服務器端保持狀態的方案在客戶端也需要保持一個標識,所以session機制可能需要借助於cookie機制來達到保存標識的目的。session還提供了方便管理全局變量的方式。session是針對每一個用戶的,變量的值保存在服務器上,用一個sessionID來區分哪個用戶的session。
1.4,cookie與session的實現原理
上圖很明顯的展示了Django的session和cookie的實現原理。服務器會生成兩份相同的cookie字符串,一份保存在本地,一份發向請求的瀏覽器。瀏覽器將會受到的cookie字符串保存下來,當下次再發請求時,會將信息與這段cookie一同發送到服務器,服務器得到這段cookie會與本地保存的那份判斷是否相同,如果相同就表示用戶已經登錄成功,保存用戶登錄成功的狀態。Django的session保存在數據庫中的數據相當於一個大字典,key為cookie的字符串,value仍是一個字典,字典的key和value為用戶設置的相關信息,這樣就可以方便的存取session里面的信息。
1.5,cookie和session內容
cookie內容主要包括:key,value,過期時間,路徑和域。路徑和域一起構成了cookie的作用范圍。
cookie的使用由瀏覽器按照一定的規則在后台自動發送給服務器的,瀏覽器檢查所在存儲的cookie,如果某個cookie的作用范圍大於等於將要請求的資源所在位置,將cookie附在請求資源的HTTP請求頭上發給服務器。
cookie的生命周期跟過期時間相關,如果不設置過期時間,成為會話cookie,保存在內存中,關閉瀏覽器窗口,cookie消失。如果設置過期時間,瀏覽器會把cookie寫入硬盤,這些cookie仍然有效,知道超過設定的過期時間。
存在硬盤上的cookie可以在同一個瀏覽器不同進程間共享,比如兩個Chrome窗口。存在內存的,不同瀏覽器不同的處理方式。
session內容,服務器使用類似Python字典的key value 的形式存儲,存儲session方式很多,數據庫,緩存,文件,加密cookie,數據庫加緩存。
1.6,HTTP無狀態性
HTTP被設計為“無狀態”,每次請求都處於相同的空間中。在一次請求和下一次情況之間沒有任何狀態保持,我們無法根據請求的任何方面(IP地址,用戶代理等)來識別同一人的連續請求,所以HTTP協議不具備保存之前發送過的請求或響應的功能。
協議對於事務處理沒有記憶能力,對同一個URL請求沒有上下文關系,每次的請求都是獨立的,它的執行情況和結果與之前的請求和之后的請求時無直接關系的。它不會受前面的請求應答情況直接影響,也不會直接影響后面的請求應答情況。服務器中沒有保存客戶端的狀態,客戶端必須每次帶上自己的狀態去請求服務器。
如果說每一一種機制用來處理無狀態,那么就需要花費時間不停的進行身份驗證,正是這樣,HTTP引入了session和cookie,即保持了HTTP的無狀態性,也使得HTTP的應用稱為有狀態的。
二,Cookie概述
2.1,什么叫 Cookie
Cookie翻譯成中文是小甜點,小餅干的意思。在HTTP中它表示服務器送給客戶端瀏覽器的小甜點。其實Cookie是 key-value 結構,類似於一個python中的字典。隨着服務器端的響應發送給客戶端瀏覽器。然后客戶端瀏覽器會把Cookie保存起來,當下一次再訪問服務器時把Cookie再發送給服務器。
注意:cookie不能跨瀏覽器的,cookie中不能存在中文
Cookie是由服務器創建,然后通過響應發送給客戶端的一個鍵值對。客戶端會保存Cookie,並會標注出Cookie的來源(哪個服務器的Cookie)。當客戶端向服務器發出請求時會把所有這個服務器Cookie包含在請求中發送給服務器,這樣服務器就可以識別客戶端!
查看cookie
我們使用Chrome瀏覽器,打開開發者工具。
我們舉個簡單的例子。
代碼如下:
modes.py
from django.db import models # Create your models here. class UserInfo(models.Model): user = models.CharField(max_length=32) pwd = models.CharField(max_length=32)
我們給UserInfo表中插入一條記錄,比如用戶名和密碼都是123(方便起見)。
views.py
from django.shortcuts import render, HttpResponse,redirect # Create your views here. from cookie_demo.models import UserInfo def index(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') user = UserInfo.objects.filter(user=user, pwd=pwd).first() if user: # 登錄成功 ''' :return HttpResponse() :return render() :return redirect() 這三個都返回的是HTTPResponse,只不過render 和redirect 將其封裝了 ''' response = HttpResponse("OK") response.set_cookie('is_login', True) return response else: pass return render(request, 'cookie/index.html')
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> </head> <body> <form action="" method="post"> {% csrf_token %} <p>用戶名:<input type="text" name="user"></p> <p>密碼:<input type="password" name="pwd"></p> <input type="submit" value="submit"> </form> </body> </html>
我們輸入一個正確的用戶名和密碼,我們在前端cookie可以看到其信息:
在響應頭,我們也可以看到其信息:
2.2,HTTP的Cookie規范
- Cookie大小上限為4KB;
- 一個服務器最多在客戶端瀏覽器上保存20個Cookie;
- 一個瀏覽器最多保存300個Cookie
上面的數據只是HTTP的Cookie規范,但是在瀏覽器大戰的今天,一些瀏覽器為了打敗對手,為了展示自己的能力起見,可能對Cookie規范“擴展”了一些,例如每個Cookie的大小為8KB,最多可保存500個Cookie等!但也不會出現將你的硬盤占滿的可能!
注意:不同瀏覽器之間是不共享Cookie的,也就是說在你使用IE訪問服務器的時候,服務器會把Cookie發給IE,然后由IE保存起來,當你在使用FireFox訪問服務器時,不可能把IE保存的Cookie發送給服務器。
2.3,Cookie的用途
- 服務器使用Cookie來跟蹤客戶端狀態
- 保存購物車
- 顯示上次登錄名
2.4,Cookie與HTTP頭
Cookie是通過HTTP請求和響應頭在客戶端和服務器端傳遞的:
- Cookie:
請求頭,客戶端發送給服務器端;
格式:Cookie:a=A;b=B;c=C。即多個Cookie用分號離開;
- Set-Cookie:
響應頭,服務器端發送給客戶端。
一個Cookie對象一個Set-Cookie:
Set-Cookie:a=A
Set-Cookie:b = B
Set-Cookie:c=C
2.5,Cookie的覆蓋
如果服務器端發送重復的Cookie,那么會覆蓋原有的Cookie。
cookies是瀏覽器為Web服務器存儲的一小段信息。每次瀏覽器從某個服務器請求頁面時,它向服務器會送之前收到的cookies,它保存在瀏覽器下的某個文件夾下。
如果服務器端發送重復的Cookie那么會覆蓋原有的Cookie,例如客戶端的第一個請求服務器端發送的Cookie是:Set-Cookie:a=A;第二請求服務器端發送的是:Set-Cookie:a=AA,那么客戶端只留下一個Cookie,即:a=AA。
2.6,Cookie 的生命周期
cookie不只有name和value,Cookie還有生命,所謂生命就是Cookie在客戶端的有效時間,可以通過setMaxAge(int) 來設置Cookie的有效時間。以秒為單位。不設置默認為關閉窗口,Cookie結束。
- cookie. setMaxAge(-1):cookie的maxAge屬性的默認值就是-1,表示只在瀏覽器內存中存活。一旦關閉瀏覽器窗口,那么cookie就會消失。
- cookie.setMaxAge(60*60):表示cookie對象可存活1小時。當生命大於0時,瀏覽器會把Cookie保存到硬盤上,就算關閉瀏覽器,就算重啟客戶端電腦,cookie也會存活1小時;
- cookie.setMaxAge(0):cookie生命等於0是一個特殊的值,它表示cookie被作廢!也就是說,如果原來瀏覽器已經保存了這個Cookie,那么可以通過Cookie的setMaxAge(0)來刪除這個Cookie。無論是在瀏覽器內存中,還是在客戶端硬盤上都會刪除這個Cookie。
過期時間
cookie 可以有過期時間,這樣瀏覽器就知道什么時候可以刪除cookie了。如果cookie沒有設置過期時間,當用戶關閉瀏覽器的時候,cookie就自動過期了。我們可以改變SESSION_EXPIRE_AT_BROWSER 的設置來控制session框架的這一行為。缺省情況下,SESSION_EXPIRE_AT_BROWSER 設置為False,這樣,會話cookie可以在用戶瀏覽器中保持有效達SESSION_COOKIE_AGE 秒(缺省設置是兩周,即1209600秒)。如果你不想用戶每次打開瀏覽器都必須重新登錄的話,用這個參數來幫助你。如果SESSION_EXPIRE_AT_BROWSER_CLOSE 設置為 True ,當瀏覽器關閉時,Django會使cookie失效。
SESSION_COOKIE_AGE :設置cookie在瀏覽器中存活的時間
在settings.py中添加:
2.7,Django中的cookie語法
cookies是一種數據存儲技術,是將一段文本保存在客戶端(瀏覽器)的一種技術,並且可以長時間的保存。
1,設置cookie
rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect() rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密鹽',...)
普通設置:
obj.set_cookie('tile' , 'name' , expires = value , path = '/')
加鹽設置(普通cookie是明文傳輸的,可以直接在客戶端直接打開,所以需要加鹽,解鹽之后才能查看):
obj.set_signed_cookie('k' , 'v' , salt = 'name' )
我們可以查看源碼,發現HttpResponse() , render(),redirect() 都返回的是HttpResponse。
2,cookie參數介紹:
obj.set_cookie('tile', 'james', expires=value ,path='/', domain=None, secure=False, httponly=False)
參數:
- 1,max_age = 1 : cookie生效的時間,單位是秒
- 2,expires:具體過期日期
- 3,path = '/' : 指定哪個url 可以訪問到cookie,‘/’ 表示所有,即 path = '/'
- 4,domain = None(None代表當前域名):指定哪個域名以及它下面的二級域名(子域名)可以訪問這個cookie
- 5,secure = False :https安全相關
- 6,httponly = False:限制只能通過HTTP傳輸,JS無法在傳輸中獲取和修改。
class HttpResponseBase: def set_cookie(self, key, #鍵 value = '', #值 max_age = None, #超長時間cookie需要延續的時間(以秒為單位)如果參數 #是\ None,這個cookie會延續到瀏覽器關閉為止。 expires = None, #超長時間,expires默認None, cookie失效的實際日期 / 時間。 path = '/', #Cookie生效的路徑,瀏覽器只會把cookie回傳給帶有該路徑的頁面, # 這樣可以避免將 cookie傳給站點中的其他的應用。 # / 表示根路徑,特殊的:根路徑的cookie可以被任何url的頁面訪問 domain = None, # Cookie生效的域名你可用這個參數來構造一個跨站cookie。 # 如, domain = ".example.com"所構造的cookie對下面這些站點都是可讀的: # www.example.com 、 www2.example.com和an.other.sub.domain.example.com 。 #如果該參數設置為None ,cookie只能由設置它的站點讀取。secure = False, # 如果設置為True ,瀏覽器將通過HTTPS來 回傳cookie。 httponly = False #只能http協議傳輸,無法被JavaScript獲取 # (不是絕對,底層抓包可以獲取到也可以被覆蓋) ): pass
3,獲取cookie
普通獲取:
request.COOKIES.get('k')
加鹽獲取:
cookies = request.get_signed_cookie('k' , salt = 'name' )
4,刪除cookie
response.delete_cookie("cookie_key",path="/",domain=name)
代碼:
def logout(request): rep = redirect("/login/") # 刪除用戶瀏覽器上之前設置的user cookie的值 rep.delete_cookie("user") return rep
5,嘗試給每個視圖函數裝飾cookie認證功能
from django.shortcuts import render,redirect,HttpResponse import datetime from until import mysqlhelper # Create your views here. def cookie_auth(func): def weaper(request, *args, **kwargs): cookies = request.get_signed_cookie('k', salt='james') if cookies == 'v': return func(request) else: return HttpResponse("NG") return weaper now = datetime.datetime.utcnow() delta = datetime.timedelta(seconds=10) def login(request): if request.method == 'GET': return render(request, 'login.html') else: username = request.POST.get('N') password = request.POST.get('P') if username == 'james' and password == '123': obj = redirect('/modal') # obj.set_cookie("tile", "james", max_age = 1,, ) value = now + delta obj.set_cookie('tile', 'james', expires=value ,path='/', domain=None, secure=False, httponly=False) obj.set_signed_cookie('k', 'v', salt='james', ) return obj else: return render(request, 'login.html') def test(request): return render(request, 'layout.html') @cookie_auth def modal(request): sql = ''' SELECT teacher.id as tid,teacher.`name`as tname,class.title FROM day64.teacher LEFT JOIN teacher_class ON day64.teacher.id=day64.teacher_class.tid LEFT JOIN day64.class ON day64.teacher_class.cid=day64.class.id; ''' teacher_list = mysqlhelper.get_list(sql, []) res = {} for row in teacher_list: tid = row['tid'] if tid in res: res[tid]['titles'].append(row['title']) else: res[tid] = {'tid':row["tid"],'tname':row["tname"],'titles':[row["title"],]} class_list=mysqlhelper.get_list("SELECT id ,title FROM day64.class" ,[]) return render(request,'modal.html',{"list":res.values(),"class_list":class_list} )
2.8,實例:保存上次訪問時間
我們要做的就是顯示上次訪問時間。
views.py
from django.shortcuts import render, HttpResponse,redirect # Create your views here. from cookie_demo.models import UserInfo def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') user = UserInfo.objects.filter(user=user, pwd=pwd).first() if user: # 登錄成功 response = HttpResponse("OK") response.set_cookie('is_login', True) import datetime date = datetime.datetime(year=2019, month=5, day=28, hour=9, minute=17) response.set_cookie('username', user.user,expires=date) return response else: pass return render(request, 'cookie/login.html') def index(request): print('index: ', request.COOKIES) is_login = request.COOKIES.get('is_login') if is_login: username = request.COOKIES.get('username') import datetime now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') last_time = request.COOKIES.get('last_visit_time', "") response = render(request, 'cookie/index.html', {'username': username, 'last_time': last_time}) response.set_cookie('last_visit_time', now) return response else: return redirect('/cookie/login/')
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>Hi, {{ username }}</h3> <p>上次登錄時間: {{ last_time }}</p> </body> </html>
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> </head> <body> <form action="" method="post"> {% csrf_token %} <p>用戶名:<input type="text" name="user"></p> <p>密碼:<input type="password" name="pwd"></p> <input type="submit" value="submit"> </form> </body> </html>
效果:
2.9,實例:Cookie版登陸校驗
views視圖代碼:
from django.shortcuts import render, HttpResponse, redirect import datetime # Create your views here. # 登錄驗證裝飾器 def login_auth(func): def inner(request, *args, **kwargs): # 獲取請求的全路徑 url = request.get_full_path() # /shopping/?nana=ppp # 服務器先拿到cookie,如果拿到則直接跳轉到shopping或者order視圖頁面 is_login = request.COOKIES.get('is_login') # xxx = request.get_signed_cookie('xxx' ,salt='123') # 拿到cookie 判斷是不是登錄狀態 if is_login: # 是登錄狀態直接調用被裝飾的函數,返回我們想要的頁面 ret = func(request, *args, **kwargs) else: # 服務端如果拿不到正確的cookie就會重定向了login這個頁面,但是帶了一些參數 return redirect('/login/?next=%s'%url) # 當清楚瀏覽器緩存是直接登錄http://127.0.0.1:8000/shopping/,會跳轉到登錄頁面 # 並且在url后面拼上http://127.0.0.1:8000/login/?next=/shopping/ # 127.0.0.1:8000/login/?next=/shopping/?nana=ppp return ret return inner def login(request): if request.method == 'POST': # 裝飾器中redirect('/login/?next=%s'%url)通過get就可以拿到next的值 # url = request.GET.get('name') username = request.POST.get('username') password = request.POST.get('password') if username == 'james' and password == '123': # 登錄成功后跳轉到該頁面 # obj = redirect(url) obj = HttpResponse("登錄成功") # 當我們不指定path時,被裝飾的視圖函數頁面只登錄后都可以直接被訪問,而不需要重新登錄 obj.set_cookie('is_login', True) # 瀏覽器只會把cookie會傳給帶有該路徑的頁面 # 也就說當我們登錄成功后,訪問shopping頁面時可以直接登錄 # obj.set_cookie('is_login', True, path='/shopping/') # 但是我們訪問order頁面時,仍然會跳到登錄頁面讓我們重新登錄 now = datetime.datetime.now().strftime('%Y-%m-%d %X') # 設置cookie,在瀏覽器的請求頭中的cookie中我們可以看到我們設置的額cookie # 並將登錄時間也寫到cookie中 obj.set_cookie('last_time', now) # 在瀏覽器中打開可以看到一個key obj.set_cookie('username','james') obj.set_cookie('password','123') # 獲取cookie,並對cookie進行加鹽,加鹽后的123 是進行加密的遺傳字符嗎 obj.set_signed_cookie('james','durant',salt='123') # 登錄成功后對其進行重定向 return obj else: obj = HttpResponse("登錄失敗") return obj return render(request, 'login.html') # 退出登錄 def logout(request): obj = HttpResponse("注銷成功") # 登錄成功獲得服務端發送來的cookie,刪除cookie, # 這樣我么在瀏覽器中在輸入 http://127.0.0.1:8000/shopping/ # J就會在此跳到登錄頁面讓我門重新登錄 return obj # 購物頁面 def shopping(request): return render(request, 'shopping.html', locals()) # locals()用法:locals()可以直接將函數中所有變量全部傳給模板 # 當然這可能會傳遞一些多余的參數,有點浪費內存的嫌疑 # 登錄成功后再請求shopping頁面,在請求頭就可以看到cookie中帶有鍵值對的形式: # Cookie: csrftoken = ybQexReWXIWaO0If0p0I7NubfBSvjUlSfR2EWWX8eKCwXbL8lDo4Kk3ar6Nbr5ZR; # last_time = "2018-09-13 18:13:17"; # name = james; # pwd = 123; # xxx = xxxxxx:1g0Ocf: 1kRB9Q0FVXlVzstrRbm4fJ61eF0 # 訂單頁面 @login_auth def order(request): return render(request, 'order.html', locals())
url路由配置
from django.contrib import admin from django.conf.urls import url # 需要先導入對應的app的views文件 from cookieapp import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.login), url(r'^order/', views.order), url(r'^shopping/',views.shopping), url(r'^logout/', views.logout), ]
template模板代碼
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="password" name="password"></p> <input type="submit"> </form> </body> </html>
shopping.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>購物車頁面</h1> </body> </html>
order.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <H1>訂單頁面</H1> </body> </html>
結果展示:(登陸頁面:127.0.0.1:8000/login.login)
登陸成功:
cookie展示
三,session概述
session就是在服務器端的“Cookie”,將用戶數據保存在服務器端,遠比保存在用戶端要安全,方便和快捷的多。Session依賴於Cookie,但是與Cookie不同的地方就是在於session將所有的數據都放在服務器端,用戶瀏覽器的cookie中只會保存一個非明文的識別信息,比如哈希值。
Session是服務器端技術,利用這個技術,服務器在運行時可以為每一個用戶的瀏覽器創建一個其獨享的session對象,由於session為用戶瀏覽器獨享,所以用戶在訪問服務器的web資源時,可以把各自的數據放在各自的session中,當用戶再去訪問該服務器中的其他web資源時,其他web資源再從用戶各自的session中取出數據為用戶服務。
Django的Session機器會向請求的瀏覽器發送cookie字符串。同時也會保存在本地一份,用來驗證瀏覽器登錄是否為同一用戶。他存在於服務器,Django默認會把session存入數據庫中。
Session依賴於Cookie,如果瀏覽器不能保存cookie,那么session就失效了。因為他需要瀏覽器的cookie和session做對比。session就是用來在服務器端保存用戶的會話狀態。
3.1,Django中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() 會調用這個方法
- 獲取session:request.session[key]
- 設置session:request.session[key] = value
- 刪除session:del request[key]
1、設置Sessions值 request.session['session_name'] ="admin" 2、獲取Sessions值 session_name = request.session["session_name"] 3、刪除Sessions值 del request.session["session_name"] 4、flush() 刪除當前的會話數據並刪除會話的Cookie。 這用於確保前面的會話數據不可以再次被用戶的瀏覽器訪問 5、get(key, default=None) fav_color = request.session.get('fav_color', 'red') 6、pop(key) fav_color = request.session.pop('fav_color') 7、keys() 8、items() 9、setdefault() 10 用戶session的隨機字符串
用戶session的隨機字符串舉例:
request.session.session_key # 將所有Session失效日期小於當前日期的數據刪除 request.session.clear_expired() # 檢查 用戶session的隨機字符串 在數據庫中是否 request.session.exists("session_key") # 刪除當前用戶的所有Session數據 request.session.delete("session_key") request.session.set_expiry(value) *如果value是個整數,session會在些秒數后失效。 *如果value是個datatime或timedelta,session就會在這個時間后失效。 *如果value是0, 用戶關閉瀏覽器session就會失效。 *如果value是None, session會依賴全局session失效策略。
3.2,session配置
Django默認支持Session,並且默認是將Session數據存儲在數據庫中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默認) 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,默認修改之后才保存(默認)
3.3,session的作用
session下次通過cookie中的sessionID(鍵)獲取用戶信息值(值)
1,會話保持,記住用戶的登錄狀態(WEB網站,分布式架構)
2,避免了敏感信息保存在客戶端,防止客戶端修改cookie信息(和cookie的區別)
session的過期時間
session的過期時間:django默認設置是2周 ,如果session過期,瀏覽器再攜帶之前的cookie就不能免登陸了。因為cookie已經失效了。
前端:如果超出了session的過期時間,那么瀏覽器會自動將對應的cookie刪除掉
后端:django沒有對過期的session做任何處理
如何刪除后台保留的一些過期的session信息
python manage.py clearsessions
當然,如果用戶在過期時間內主動退出登錄,那么django會將該用戶對應的session數據給刪除掉 (request.session.flush())
但是如果用戶在登錄完以后沒有主動退出,並且超出了過期時間,用戶需要重新登錄,但django中的過期session是不清除的,需要定期清理過期的session數據。
3.4,session ID的作用
當客戶端第一次請求session時,服務器端會為客戶端創造一個session對象,並且生成一個session ID (通過加密算法)。然后保存在cookie中,當用戶再次登錄時,客戶端通過cookie,將session ID傳到服務器,去和服務器中的session ID進行比對,尋找這個session,然后根據查找結果執行對應的操作。
因為session ID 是保存在cookie中,而cookie是存在於客戶端,所以session ID 並不安全。
為了避免session ID 被盜,我們可以這樣做:
- 1,敏感操作需要用戶輸入密碼來進行二次認證
- 2,網站HTTPS化,提高消息傳遞過程中的安全系數
- 3,用戶使用一個密鑰對參數進行hash,這樣即使cookie被盜取,也會因為沒有密鑰而無法獲取sessionID。
3.5,session的保存方式
Session是大多數網站都需要具備的功能,Django為我們提供了一個通用的session框架,並且可以使用多種session數據的保存方式:
- 保存在數據庫內
- 保存到緩存
- 保存到文件內
- 保存到cookie內
通常情況下,沒有特別需求的話,請使用保存在數據庫內的方式,盡量不要保存在cookie內。
django的session框架支持匿名會話,封裝了cookies的發送和接收過程。cookie包含一個會話ID而不是數據本身(除非你使用的是基於后端的cookie)。
django的會話框架完全的,唯一的基於cookie。它不像PHP一樣,把會話的ID放在URL中,那樣不僅使得URL變得丑陋,而且使得你的網站易於受到通過“Referer”頭部進行竊取會話ID的攻擊。
3.6,session流程解析
session的應用要依賴於cookie(session就是cookie的變種)
1,每次用戶第一次訪問服務端,把用戶的唯一字符串session_id加到cookie里面發送給客戶端;
當用戶登錄之后,生成一個字典{key : value},將字典存入session,key是自動生成的一段字符串表示,返回cookie,value是一個自定義格式的字典。
2,服務器端保存隨機字符串(sessionID :{用戶信息}) 服務端
當我們在django中用到session時,cookie由服務端隨機生成,寫到瀏覽器的cookie中。每個瀏覽器都有自己的cookie值,是session尋找用戶信息的唯一標識。每個瀏覽器請求到后台接受的request.session 等價於在1中的session字典key (cookie)對應的value。
3,session的好處就是客戶端只有cookie的值,但是始終沒有用戶信息。
用法:request.session.get('k1') request.session['k1'] = 'v1'
session依賴於cookie,cookie保存在瀏覽器。session保存在服務器 端。
3.7,示例
注意:這里需要注意在session中,我們可以設置多個key:value的值,方便我們做很多事情,比如判斷哪個用戶:
如果用戶登錄后,那么他肯定有一個cookie,而當他訪問購物車的時候,怎么判斷是哪個用戶呢?我們可以在session設置,當用戶登錄的時候,我們把用戶名增加到session中,那么用戶攜帶cookie訪問的時候,我們就能判斷是哪個用戶來訪問的。
比如下面的對應關系:
user1
cookie : 001
server session(舉例格式)
{session:001 {'IS_LOGIN':'True',username:'james'}}
3.7.1,一段簡單的Django中實現session的代碼,判斷用戶是否已經成功登陸
from django.shortcuts import render,redirect,HttpResponse # Create your views here. def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'james' and password == '123': # 設置session request.session['IS_Login'] = True return redirect('/app01/home') return render(request, 'login.html') def home(request): # 獲取session is_login = request.session.get("IS_LOGIN", False) if is_login: return HttpResponse('order') else: return redirect('/app01/login/')
3.7.2 在視圖中使用session
def session_test(request): # 設置session值,會將其存到db.sqlite數據庫中的django_session表中 # 每發送一次請求就會在django_session表中添加一條記錄 request.session['username'] = 'james' request.session['is_login'] = True ''' 1,生成一個隨機字符串 2,把隨機字符串以cookie的形式寫回給瀏覽器 3,會在數據庫中存{'隨機字符串': {'username' : 'james' , 'is_login': True} ''' return HttpResponse("ok") def get_session(request): # 在瀏覽器中輸入路由地址就會訪問訪問該視圖函數 # 獲取session值,但是一定要先獲取session值,否則獲取session時會報錯 # name=request.session['username'] # is_login=request.session['is_login'] # print(name) # print(is_login) # session取值的兩種方式,字典key或者get進行取值 # request.session['k1'] # request.session.get('k1', None) # 設置session的值 # request.session['k1'] = 123 # name=request.session.setdefault('username', 123) # 如果session中的username有值就不會設置,無值則進行設置 # print(name) #-----123 # del request.session['k1'] # 通過key將session的值進行刪除 # print(request.session.session_key) # 取出表django_session表中session_key字段對應的值 # 將所有Session失效日期小於當前日期的數據刪除 # request.session.clear_expired() # 判斷session表中session_key字段的值是否存在,返回值為布爾值 # print(request.session.exists("b16mh23xajc2u69vazvivf8ruo4ilumi")) # 會刪除數據庫的session表中的session的記錄,但是瀏覽器中的session id還在 # request.session.delete() # 刪除當前的會話數據並刪除會話的Cookie。 # 通常用它,他會把數據庫以及瀏覽器會話中的session id都刪除 # (調用視圖函數test可以看到效果的確是已經被刪除) request.session.flush() return HttpResponse("OK")
flush()方法是比較安全的一種做法,而且一次性將session中的所有內容全部清空,確保不留后患,但是也有不好的地方,那就是如果你在session中夾帶了一點“私貨”,會被一並刪除,這一點一定要注意。
比如登出視圖函數:
def logout(request): if not request.session.get('is_login', None): # 如果本來就未登錄,也就沒有登出一說 return redirect("/index/") request.session.flush() # 或者使用下面的方法 # del request.session['is_login'] # del request.session['user_id'] # del request.session['user_name'] return redirect("/index/")
3.7.3 在視圖函數中使用裝飾器裝飾CBV
from django.utils.decorators import method_decorator # 在視圖函數中裝飾類 需要先導入method_decorator模塊 from django.views import View # 在類上加裝飾器需要指定name的值,也就是要指定裝飾類內的那個函數 # 當我們登錄成功后就會直接跳轉到我們要登錄的頁面 # @method_decorator(login_auth, name='post') # @method_decorator(login_auth, name='get') class MyOrder(View): # 將裝飾器傳入進去 # @method_decorator(login_auth) # 我們可以重寫dispatch函數來實現 def dispatch(self, request, *args, **kwargs): # 我們可以重寫dispatch函數來實現類似於裝飾器的效果 # dispatch 內部根據反射來實現函數執行 # 集成父類view的屬性 ret = super().dispatch(request, *args, **kwargs) return ret # 在cbv上加裝飾器,需要用method_decorator修飾一下 @method_decorator(login_auth) def get(self, request): return HttpResponse('get') def post(self,request): return HttpResponse('post') ''' 1 導入from django.utils.decorators import method_decorator 2 加載get,post,dispatch方法上:@method_decorator(login_auth) 3 加在類上:@method_decorator(login_auth,name='get') '''
四,cookie + session
1,cookie引入session
cookie看似解決了HTTP(短連接,無狀態)的會話保持問題,但把全部用戶數據保存在客戶端,存在安全隱患。
於是cookie+session出現了,我們,我們可以把關於用戶的數據保存在服務端,在客戶端cookie里加一個sessionID(隨機字符串)。
基於以上原因:cookie+session組成就成立了,並且結束了單單使用cookie做會話保持的方式。
2,Session和Cookie的好處
使用Session和Cookie的好處:Cookie可以理解為一個身份證ID,你只要拿着他去和Server端進行通信,如果你沒有這個ID,那么server端也不知道你是誰。
舉個例子:我在寫博客的時候,在做Cookie和Session的實驗,把Cookie刪掉了,當我保存的時候直接給我提出來了,為什么呢?就是因為server端不知道我是誰了,因為我已經沒有秘鑰了。
所以,只要Session和Cookie任意一方失效,就可以理解為:Cookie失效相當於身份證ID過期,需要重新認證才可以繼續使用。Session失效就相當於銀行里的數據標識此ID無效,也需要重新申請。
3,cookie+session 的工作流程
1,當用戶來訪問服務端的時候,服務端生成一個隨機字符串;
2,當用戶登錄成功后,把{sessionID : 隨機字符串} 組織成鍵值對 加到cookie里發送給用戶;
3,服務器以發送給客戶端cookie中的隨機字符串做鍵,用戶信息做值,保存用戶信息;
代碼梳理
用戶登錄的兩種方式:
1,使用login() 和logout()這兩個內置函數實現登錄和退出;缺點就是用戶的登錄時在操作同一個request.user。導致同一台電腦上不能同時登錄兩個用戶;
2,如果同一台電腦的同一個網站,登錄多個賬號,為了防止串號,不能再使用login() 和logout()函數了,可以通過session和cookie來實現這個需求;
1,login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="login.html" method="post"> <input type="text" name="username"> <input type="password" name="pwd"> <input type="submit" value="submit"> </form> </body> </html>
2,home.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .header{ height: 48px; } </style> </head> <body> <div class="header"> <div style="float: right;">{{ username }}</div> <div style="float:right;"><a href="logout.html">注銷</a></div> </div> <div style="height: 500px; background-color: #ddd"></div> </body> </html>
3,view.py
from django.shortcuts import render,redirect,HttpResponse # Create your views here. def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'james' and password == '123': # 設置session request.session['IS_Login'] = True request.session['USERNAME'] = 'james' return redirect('/Manytable/home/') elif username == 'durant' and password == '123': request.session['IS_LOGIN'] = True request.session['USERNAME'] = 'durant' return redirect('/Manytable/home/') return render(request, 'Manytable/login.html') def home(request): # 獲取session is_login = request.session.get("IS_LOGIN", False) if is_login: username = request.session.get("USERNAME", False) return render(request, 'Manytable/home.html', {'username':username}) # return HttpResponse('order') else: return redirect('/Manytable/login/')
4,mysite/urls.py
from django.contrib import admin from django.urls import path from django.conf.urls import url # 需要先導入對應的app的views文件 from Manytable import views # admin 后台的路由,先注釋掉 urlpatterns = [ url(r'^admin/', admin.site.urls), # 你的路由,重點是引號中的正則表達式和后面的業務邏輯函數 url(r'^login/',views.login), url(r'^index/',views.home), ]
5,格式如下:
作業練習
1,登錄案例
需要的頁面: #login.html:登錄頁面,提供登錄表單; #index1.html:主頁,顯示當前用戶名稱,如果沒有登錄,顯示您還沒登錄; #index2.html:主頁,顯示當前用戶名稱,如果沒有登錄,顯示您還沒登錄;
思考:如果第二個人在同一個瀏覽器上登錄,Django-session表會怎么樣呢?
2,驗證碼案例
驗證碼可以去識別發出請求的是人還是程序!當然,如果聰明的程序可以去分析驗證碼圖片!但是分析圖片也不是一件容易的事情,因為一般驗證碼圖片都會帶有干擾線,人都看不清,那么程序一定分析不出來。
參考文獻:https://www.cnblogs.com/chenchao1990/p/5283725.html
https://blog.csdn.net/qq_32446743/article/details/79482536
https://www.jianshu.com/p/59cb3ecd81a4
https://www.cnblogs.com/sui776265233/p/9643055.html