Django學習筆記(5)——cookie和session


一,前言

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM