Python Django 學習紀錄(六) Cookies 與 Sessions

 

  • Cookie 是將狀態資料記錄在用戶端電腦的技術
  • Session 則會將使用者資訊儲存在伺服器端站存檔中

 

一、關於 Cookie 與 Session:

 

1.為什麼要使用Cookie 和 Session ?

Cookie 和 Session 的存在就是為了要解決網站不能保存狀態的問題,以會員系統來舉例:

  • Cookie  :可以在登入者的電腦中放入一個小檔案來記憶
  • Session :可以在伺服器的記憶體產生一個空間來記憶

2. Cookie 與 Session 的比較

(1)關於Cookies

Cookie是儲存在瀏覽者電腦中的小檔案,可以保持一段較長的時間,搭配程式的應用即可避免重複輸入資料的麻煩,例如、登入會員系統

Cookie 有以下限制:

  1. 每個使用者的瀏覽器只能儲存使用 300 個 Cookie
  2. 每個瀏覽器只能針對同一個伺服器存取 20 個 Cookie
  3. 每個 Cookie 的大小最多僅 4k Bytes 的容量
  4. 瀏覽器可以設定關掉 Cookie 的功能,但可能會造成 Cookie 無法使用

Cookie 關於資訊安全的問題:

  • 是以明碼的方式儲存在使用者電腦中,有可能被擷取來不當利用,例如、帳號密碼、信用卡卡號

 

(2)關於Sessions

  • Sessions則會將使用者資訊儲存在伺服器端暫存檔中,當瀏覽者進入網站伺服器瀏覽時,在 Session 開啟的狀態下即會開始記錄使用者所賦予的資訊,直到有效期限結束或刪除 Session
  • Session 是存在伺服器端的,所以在安全性的考量下會選用 Session ,較不易遭人利用以進行其他的操作
  • Session 因為存在伺服器端,即使用戶端的瀏覽器關閉 Cookie 的使用, Session 仍然可以正常運作

二、Cookie 的使用:

 

Cookie 是將狀態資料記錄在用戶端電腦的技術,當瀏覽者開啟網站時,即可在程式的設定下將指定的資料儲存在獄戶端的電腦中,並可設定該資訊的有效時間。

1.存取 Cookie 資料

(1)允許儲存及讀取 Cookie

這邊將以Chrome瀏覽器為範例,在進階設定中可以設定是否允許網站的儲存及讀取:

49.JPG 

 

(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 專案:

  1. 創建一個名為 CookieSession 的專案
  2. 建立名為 CookieSessionApp 的 App 
  3. 建立 templates 目錄、static 目錄
  4. 建立 makemigrations 資料檔,並利用 migrate 將模型與資料庫同步
  5. 最後完成<setting.py的設定>

50.JPG

 以下為<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」。

網頁執行結果如下:

51.JPG

52.JPG

 

輸入「127.0.0.1:8000/get_cookie/name」即可取得名稱為「name」的 Cookie 內容

53.JPG

 

(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,網頁執行結果如下:

55.JPG

 

 

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小時

57.JPG

 

關閉瀏覽器後再輸入「127.0.0.1:8000/get_cookie/name2/」,因為其存在時間文1小時,所以依然可以取得名為 name2 的 Cookie 內容

59.JPG

輸入「127.0.0.1:8000/delete_cookie/name2/」來刪除cookie 並設定名為「name2」的 Cookie 內容

58.JPG

 

範例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次):

60.JPG

 

三、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:

61.JPG

輸入「http://127.0.0.1:8000/get_session/who/」來顯示 Session:

※它的名稱為 sessionid,內容則是一個編碼過的字串

62.JPG

 

 

(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:

63.JPG

 

 

 

範例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/」來投票:

64.JPG

重整瀏覽器後:

65.JPG

 

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:

66.JPG

輸入「http://127.0.0.1:8000/delete_session/who2/」來刪除剛剛儲存的 Session:

67.JPG

 

範例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路徑(或是去掉之前的註解)

以下為執行結果:

68.JPG

69.JPG

70.JPG

arrow
arrow
    全站熱搜

    ivankao 發表在 痞客邦 留言(0) 人氣()