Python Django 學習紀錄(六) Cookies 與 Sessions
- Cookie 是將狀態資料記錄在用戶端電腦的技術
- Session 則會將使用者資訊儲存在伺服器端站存檔中
一、關於 Cookie 與 Session:
1.為什麼要使用Cookie 和 Session ?
Cookie 和 Session 的存在就是為了要解決網站不能保存狀態的問題,以會員系統來舉例:
- Cookie :可以在登入者的電腦中放入一個小檔案來記憶
- Session :可以在伺服器的記憶體產生一個空間來記憶
2. Cookie 與 Session 的比較
(1)關於Cookies
Cookie是儲存在瀏覽者電腦中的小檔案,可以保持一段較長的時間,搭配程式的應用即可避免重複輸入資料的麻煩,例如、登入會員系統
Cookie 有以下限制:
- 每個使用者的瀏覽器只能儲存使用 300 個 Cookie
- 每個瀏覽器只能針對同一個伺服器存取 20 個 Cookie
- 每個 Cookie 的大小最多僅 4k Bytes 的容量
- 瀏覽器可以設定關掉 Cookie 的功能,但可能會造成 Cookie 無法使用
Cookie 關於資訊安全的問題:
- 是以明碼的方式儲存在使用者電腦中,有可能被擷取來不當利用,例如、帳號密碼、信用卡卡號
(2)關於Sessions
- Sessions則會將使用者資訊儲存在伺服器端暫存檔中,當瀏覽者進入網站伺服器瀏覽時,在 Session 開啟的狀態下即會開始記錄使用者所賦予的資訊,直到有效期限結束或刪除 Session
- Session 是存在伺服器端的,所以在安全性的考量下會選用 Session ,較不易遭人利用以進行其他的操作
- Session 因為存在伺服器端,即使用戶端的瀏覽器關閉 Cookie 的使用, Session 仍然可以正常運作
二、Cookie 的使用:
Cookie 是將狀態資料記錄在用戶端電腦的技術,當瀏覽者開啟網站時,即可在程式的設定下將指定的資料儲存在獄戶端的電腦中,並可設定該資訊的有效時間。
1.存取 Cookie 資料
(1)允許儲存及讀取 Cookie
這邊將以Chrome瀏覽器為範例,在進階設定中可以設定是否允許網站的儲存及讀取:
(2)儲存 Cookie 資料
可以使用 HttpResponse 物件的 set_cookie() 函式將資料存入 Cookies 中,格式為:
set_cookie(key,value='',max_age=None,expires=None)
參數 | 說明 |
key | Cookie 儲存時的名稱 |
value | Cookie 儲存值以URL編碼,以明碼的方式儲存 |
max_age | Cookie 的持續時間,單位為秒,省略時間則預設為瀏覽器開啟時間,關閉瀏覽器 Cookie 即自動刪除 |
expires | 到期時間,是一個UTC格式的字符串,或是一個datetime.datetime物件 |
例如:以HttpResponse 建立 response 物件,再以 response 物件的 set_cookie 方法儲存 Cookie
from django.http import HttpResponse response = HttpResponse('TestCookie') response.set_cookie("TestCookie","這是 Cookie 內容",max_age=3600)
(3)讀取 Cookie 資料
可以使用 Cookie 函式以字典的方式讀出 Cookie 中儲存的資料,格式為:
request.COOKIES[名稱]
例如、讀出 TestCookie 中儲存的資料:
request.COOKIES["TestCookie"]
※範例.建立 CookieSession 專案:
- 創建一個名為 CookieSession 的專案
- 建立名為 CookieSessionApp 的 App
- 建立 templates 目錄、static 目錄
- 建立 makemigrations 資料檔,並利用 migrate 將模型與資料庫同步
- 最後完成<setting.py的設定>
以下為<setting.py>設定:
import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'w2mdamhgqk$q)rk$8n7(^or_wr&f=-awd$0rc@qc!1$jt-d83e' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'CookieSessionApp', #新增的App ] 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', ] ROOT_URLCONF = 'CookieSession.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR,'templates')], #加上templates路徑 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'CookieSession.wsgi.application' # Database # https://docs.djangoproject.com/en/2.0/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } # Password validation # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ LANGUAGE_CODE = 'zh-Hant' #設定語系 TIME_ZONE = 'Asia/Taipei' #設定時區 USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ]
接著在 <urls.py> 定義下列的 URL:
先將還不會用到的路徑註解掉,之後要使用時再陸續打開,以免之後運行伺服器會產生找不到路徑中函數的問題:
from django.contrib import admin from django.urls import path from django.urls import re_path from CookieSessionApp import views urlpatterns = [ path('admin/', admin.site.urls), # re_path('set_cookie/(\w+)/(\w+)/$', views.set_cookie), # re_path('set_cookie2/(\w+)/(\w+)/$', views.set_cookie2), # re_path('get_cookie/(\w+)/$', views.get_cookie), # path('get_allcookies/', views.get_allcookies), # re_path('delete_cookie/(\w+)/$', views.delete_cookie), # path('', views.index), # path('index/', views.index), # re_path('set_session/(\w+)/(\w+)/$', views.set_session), # re_path('get_session/(\w+)/$', views.get_session), # path('get_allsessions/', views.get_allsessions), # path('vote/', views.vote), # re_path('set_session2/(\w+)/(\w+)/$', views.set_session2), # re_path('delete_session/(\w+)/$', views.delete_session), # path('login/', views.login), # path('logout/', views.logout), ]
範例1.儲存並顯示Cookie 值:
欲達成目標:
- 儲存並顯示 Cookie 值
- 輸入「127.0.0.1:8000/set_cookie/名稱/內如」來儲存cookie
- 輸入「127.0.0.1:8000/get_cookie/名稱」取得該cookie名稱的 Cookie 內容
請打開 CookieSessionApp 裡的<view.py>寫一個名為 set_cookie() 和 一個名為 get_cookie() 的自訂函數:
from django.shortcuts import render from django.http import HttpResponse #導入HttpResponse import datetime def set_cookie(request,key=None,value=None): #定義set_cookie函式,key為名稱,value為內容 response = HttpResponse('Cookie 儲存完畢') response.set_cookie(key,value) return response def get_cookie(request,key=None): if key in request.COOKIES: #檢查指定的Cookie是否存在 return HttpResponse('%s:%s' % (key,request.COOKIES[key])) else: return HttpResponse('Cookie 不存在!')
最後記得在<urls.py>新增 set_cookie 和 get_cookie 的url路徑(或是去掉之前的註解)
輸入「127.0.0.1:8000/set_cookie/name/ivan」來儲存cookie,儲存後在網頁按右鍵、檢查,
選擇Application\Cookies 中的「127.0.0.1:8000」即可看到儲存的Cookie名稱是「name」內容是「ivan」。
網頁執行結果如下:
輸入「127.0.0.1:8000/get_cookie/name」即可取得名稱為「name」的 Cookie 內容
(4) Cookie 字典
Cookie 的格式是字典,因此可以迴圈方式從字典的 item() 項目中,取得字典的 key 和 value。
範例2.顯示所有 Cookie 值:
欲達程目標:
- 定義一個get_allcookies() 自訂函數取得Cookie中所有名稱和內容,
- 存取前要以「if request.COOKIES !=None」檢查Cookie是否存在
- 輸入「127.0.0.1:8000/get_allcookies/」來顯示所有 cookie
請打開 CookieSessionApp 裡的<view.py>寫一個名為 get_allcookies() 的自訂函數:
def get_allcookies(request): if request.COOKIES != None: strcookies="" for key1,value1 in request.COOKIES.items(): strcookies= strcookies + key1 + ":" + value1 + "<br>" return HttpResponse("%s" % (strcookies)) else: return HttpResponse('Cookie 不存在!')
記得在<urls.py>新增 get_allcookies 的url路徑(或是去掉之前的註解)
先用set_cookie 再存入第二筆 「127.0.0.1:8000/set_cookie/age/22」
再輸入「127.0.0.1:8000/get_allcookies/」來顯示所有 cookie,網頁執行結果如下:
2.Cookie 的有效時間
剛才的幾個範例因沒有設定時間,預設當您關閉瀏覽器就會刪除 Cookie ,如果要保持 Cookie 的存在,就必須設定 Cookie 的持續時間。
(1)設定 Cookie 的持續時間
在 set_cookie() 函式中可以 max_age 參數設定 Cookie 的持續時間,其設定單位為秒
例如要設定持續時間為一個小時,語法如下:
response.set_cookie("TestCookie","這是內容",max_age=3600")
(2)設定 Cookie 的到期時間
在 set_cookie() 函式中可以 expires 參數設定 Cookie 的到期時間,且要導入datetime套件來判定時間
例如:設定到期時間為明日的「0:0:0」,其中 tomorrow 是當前時間加1日,也就是到明天,然後以 replace 重設時、分、秒為「0:0:0」,最後再利用strtime將日期轉換成指定的格式。 (轉換格式 可以參考此文件說明)
tomorrow = datetime.datetime.now() + datetime.timedelta(days = 1) tomorrow = datatime.datetime.replace(tomorrow, hour=0, minute=0, second=0) expires = datetime.datetime.strtime(tomorrow,"%a, %d-%b-%Y %H:%M:%S GMT") response.set_cookie("counter",counter,expires=expires)
3.刪除 Cookie
使用 HttpResponse 物件的 delete_cookie() 函式可以刪除指定 key 的 Cookie ,如果 key 不存在就什麼也不會發生:
delete_cookie[名稱]
例如、刪除 TestCookie 中儲存的資料:
response = HttpResponse('Delete Cookie') response.delete_cookie("TestCookie)
範例3.設定 Cookie 有效時間與刪除 Cookie:
欲達程目標:
- 設定 Cookie 有效時間
- 刪除指定的 Cookie
請打開 CookieSessionApp 裡的<view.py>寫一個名為 set_cookie2() 和一個名為 delete_cookie()的自訂函數:
def set_cookie2(request,key=None,value=None): response = HttpResponse('Cookie 有效時間 1 小時') response.set_cookie(key,value,max_age=3600) return response def delete_cookie(request,key=None): if key in request.COOKIES: response = HttpResponse('Delete Cookie: '+key) response.delete_cookie(key) return response
接著在<urls.py>新增 set_cookie2和 delete_cookie 的url路徑(或是去掉之前的註解)
以下為執行結果:
輸入「127.0.0.1:8000/set_cookie2/name2/clover」來儲存cookie 並設定為 1小時
關閉瀏覽器後再輸入「127.0.0.1:8000/get_cookie/name2/」,因為其存在時間文1小時,所以依然可以取得名為 name2 的 Cookie 內容
輸入「127.0.0.1:8000/delete_cookie/name2/」來刪除cookie 並設定名為「name2」的 Cookie 內容
範例4.顯示使用者今天瀏覽本頁面的次數:
欲達程目標:
- 利用 Cookie 來紀錄使用者今天瀏覽本頁的次數,若是過了今天則紀錄的次數重新歸零,重新計算
請打開 CookieSessionApp 裡的<view.py>寫一個名為 index() 的自訂函數:
def index(request): if "counter" in request.COOKIES: #若 counter 已經建立,則 counter + 1 counter = int(request.COOKIES["counter"]) counter += 1 else: #若 counter 尚未建立,則 counter = 1 counter = 1 response = HttpResponse('今日瀏覽次數:'+ str(counter)) tomorrow = datetime.datetime.now() + datetime.timedelta(days = 1) tomorrow = datetime.datetime.replace(tomorrow, hour=0, minute=0, second=0) expires = datetime.datetime.strftime(tomorrow, "%a, %d-%b-%Y %H:%M:%S GMT") #利用strftime將日期轉換為指定的格式 response.set_cookie("counter",counter,expires=expires) #以 expires 參數設定到期時間 return response
接著在<urls.py>新增 index 的url路徑(或是去掉之前的註解)
以下為執行結果:
輸入「127.0.0.1:8000/index」會顯示瀏覽本頁的次數,網頁重整的話就會累積次數,這裡已經重整4次(加第一次打開總共為5次):
三、Session 的使用:
Session 是瀏覽者與伺服器連線工作期間所保持的狀態,它的使用時間是在開啟瀏覽器後進入啟動 Session 機制的網站開始。
1. Session 的運作原理
- 當使用者使用瀏覽器連線到伺服器網站,網站會自動派發一個 SessionID 給這次的連線動作
- 網站依據 SessionID 分辨使用者來處理儲存的狀態
- 預設的情況下,Session 會將 SessionID 加密處理後已 Cookie 的方式儲存在用戶端來記錄狀態
2. 安裝 Session App
使用 Session 需安裝 Session 的 App ,當 Django專案建立完成後預設會自動安裝 Session 的 App
請打開<setting.py>檔案確認,以下為節錄:
# Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', #Session App 'django.contrib.messages', 'django.contrib.staticfiles', 'CookieSessionApp', ]
3. 存取 Session 資料
可使用 request 物件的 session() 函式,以字典的方式存取 session 資料
(1)儲存 session 資料
以字典方式可以設定 session 的值,格式如下:
request.session[名稱] = 值
(2)讀取 session 資料
儲存 session 之後可以透過該鍵名稱取得 Session 資料,格式如下:
變數 = request.session[名稱]
範例5.儲存並顯示 Session 值:
欲達程目標:
- 儲存並顯示 Session 值
請打開 CookieSessionApp 裡的<view.py>寫一個名為 set_session() 和 get_session() 的自訂函數:
def set_session(request,key=None,value=None): response = HttpResponse('Session 儲存完畢!') #建立 HttpResponse 物件 response request.session[key] = value #建立 Session return response def get_session(request,key=None): if key in request.session: #檢查指定的session是否存在 return HttpResponse('%s : %s' %(key,request.session[key])) else: return HttpResponse('Session 不存在!')
接著在<urls.py>新增 set_session 和 get_session 的url路徑(或是去掉之前的註解)
以下為執行結果:
輸入「http://127.0.0.1:8000/set_session/who/students/」來儲存 Session:
輸入「http://127.0.0.1:8000/get_session/who/」來顯示 Session:
※它的名稱為 sessionid,內容則是一個編碼過的字串
(3) Session 字典
Session 的格式是字典,可以透過迴圈 items() 的項目,取的字典 key 和 value ,以下將直接用範例介紹:
範例6.顯示所有 Session 值:
欲達程目標:
- 迴圈顯示所有顯示 Session 值
請打開 CookieSessionApp 裡的<view.py>寫一個名為 get_allsessions() 的自訂函數:
def get_allsessions(request): if request.session != None: strsessions="" for key1,value1 in request.session.items(): strsessions = strsessions + key1 +":"+ str(value1) + "<br>" return HttpResponse(strsessions) else: return HttpResponse("Session 不存在!")
接著在<urls.py>新增 get_allsessions 的url路徑(或是去掉之前的註解)
使用set_session() 在新增一筆資料 「http://127.0.0.1:8000/set_session/hello/ivan/」
以下為執行結果:
輸入「http://127.0.0.1:8000/get_allsessions/」來顯示所有 Session:
範例7. 利用 Session 防止灌票:
欲達程目標:
- 利用session防止瀏覽器重啟或重整,導致計數程式重複累加
- 因為request.session["vote"]的建立,即可防止灌票的情況發生
請打開 CookieSessionApp 裡的<view.py>寫一個名為 vote() 的自訂函數:
def vote(request): if not "vote" in request.session: request.session["vote"] = True msg = '您第一次投票!' else: msg = "您已投過票!" response = HttpResponse(msg) return response
以下為執行結果:
輸入「http://127.0.0.1:8000/vote/」來投票:
重整瀏覽器後:
4. Session 的有效時間
Session 的有效時間,可以在<setting.py>中做以下參數設定:
參數 |
意義 | 預設值 |
SESSION_EXPIRE_AT_BROWSER_CLOSE | 決定SESSION是否在瀏覽器關閉時結束。 | False |
SESSION_COOKIE_AGE | session(cookie)的有效時間。 | 1,209,600秒,兩週 |
※基本上就是在瀏覽器開啟後宣告 Session 的啟動開始,持續兩週,可以在<setting.py>中進行更改 session 的有效期限。
可以在 <view.py>的函式中,動態設定 Session 的有效時間,語法如下:
response.session.set_expiry(value)
例如、設定 Session 的持續時間為50分鐘:
response.session.set_expiry(50*60)
5.刪除 Session
可以使用下列兩個方式來刪除 Session:
1.del 指令:
del request.session[名稱]
2.clear()方法:
request.session.clear()
範例8. 設定 Session 持續時間:
欲達程目標:
- 設定 session 持續時間
- 刪除指定的 session
請打開 CookieSessionApp 裡的<view.py>寫一個名為 set_session2() 和 delete_session() 的自訂函數:
def set_session2(request,key=None,value=None): response = HttpResponse('Session 儲存完畢!') request.session[key] = value request.session.set_expiry(30) #設定持續時間為30秒 return response def delete_session(request,key=None): if key in request.session: response = HttpResponse('Delete Session: '+ key) del request.session[key] #刪除指定的 Session return response else: return HttpResponse("No Session:" + key)
接著在<urls.py>新增 set_session2 和 delete_session 的url路徑(或是去掉之前的註解)
以下為執行結果:
輸入「http://127.0.0.1:8000/set_session2/who2/clover/」來儲存一個持續30秒的 Session:
輸入「http://127.0.0.1:8000/delete_session/who2/」來刪除剛剛儲存的 Session:
範例9. 會員登入和登出系統:
欲達程目標:
- 會員登入後會產生一個 Session 值來記錄登入者帳號
- 將製作一個可以登入登出的介面供使用者登入登出
請打開 CookieSessionApp 裡的<view.py>寫一個名為 login() 和 logout() 的自訂函數:
def login(request): #預設帳號密碼 username = "ivanpotato" password= '12340000' if request.method == 'POST': #如果是由<login.html>表單傳送 if not username in request.session: #如果 username 這個 Session 尚未建立 if request.POST['username'] == username and request.POST['password'] == password: #如果帳號密碼正確 request.session['username']=username #建立username這個 Session message=username+' 您好,登入成功!' status = "login" #以 status 變數紀錄目前的狀態為login else: if 'username' in request.session: if request.session['username'] == username: message = request.session['username'] +' 您已經登入過了!' status = 'login' return render(request,'login.html',locals()) def logout(request): if 'username' in request.session: message = request.session['username'] + ' 您已登出!' del request.session['username'] #刪除 Session return render(request,'login.html',locals())
接著在temmplates目錄建立一個<login.html>:
<!DOCTYPE html> <html> <head> <title>登入登出系統</title> </head> <body> <form action="/login/" method="POST" name="form1"> {% csrf_token %} {% if status == "login" %} 如果已經登入成功,僅顯示登出系統按鈕 <div><a href="/logout/">登出系統</a></div> {% else %} <div>帳號:<input type="text" name="username" id="username" /></div> <div>密碼:<input type="text" name="password" id="password" /></div> <div><input type="submit" name="button" id="button" value="登入" /></div> {% endif %} <span style="color:red">{{message}}</span> </form> </body> </html>
記得在<urls.py>新增 login 和 logout 的url路徑(或是去掉之前的註解)
以下為執行結果: