django會話跟蹤技術

django中的會話跟蹤技術

什麼是會話跟蹤技術

首先咱們須要瞭解一下什麼是會話?咱們能夠把會話看成成客戶端與服務器之間的一次會晤,在一次會晤期間會有屢次請求和響應。例如你打電話給10086客服,那麼此時你就是客戶端,10086客服就是服務端,那麼一次會晤就是大家在打電話期間的聊天過程。直到某一方掛了電話,此時表示會話結束。在大家的通話過程當中,你會向10086發送屢次請求,那麼這些請求都會保存在一個會話中。python

在JavaWeb中,客戶端向服務器發出第一個請求開始,會話就開始了,直到客戶端關閉了瀏覽器會話結束。web

在一次會話中的多個請求須要共享數據,這就是會話跟蹤技術。例如在一個會話中的請求以下:數據庫

  • 請求銀行主頁
  • 請求登錄(請求參數是用戶名和密碼)
  • 請求轉帳(請求參數與轉帳相關的數據)
  • 請求信用卡還款(請求參數與還款相關的數據)

在以上此次會話中,當前用戶的信息必須是要在此次會話中共享的,由於登錄的是zhangsan,那麼轉帳和還款確定是用zhangsan用戶轉帳和還款,這就說明咱們必須在一個會話過程當中有共享數據的能力。django

HTTP無狀態協議

HTTP協議是一種不保存狀態,即無狀態協議。HTTP協議自身不對請求和響應之間的通訊狀態進行保存。也就是說在HTTP這個級別,協議對於發送過的請求或響應都不作持久化處理。
1瀏覽器

使用HTTP協議,每當有新的請求發送時,就會有對應的新響應產生。協議自己並不保留以前一切的請求或響應報文的信息。這是爲了更快的處理大量事務,確保協議的可伸縮性,而特地把HTTP協議設計的如此簡單的。安全

但是,隨着web的不斷髮展,因無狀態而致使業務處理變得棘手的狀況增多了。好比我們剛剛說的請求銀行、登錄、轉帳、還款的問題。雖然HTTP協議是無狀態協議,但爲了實現指望的保持狀態功能,因而引入了cookie技術。有了cookie再用HTTP協議通訊,就能夠管理狀態了。服務器

Cookie概述

什麼是cookie

cookie翻譯成中文是小甜點、小餅乾的意思。在HTTP中它表示從服務器送給客戶端的小甜點。其實cookie是key-value結構,和python的字典比較相似。隨着服務器端的響應發送給客戶端瀏覽器,而後客戶端瀏覽器會把cookie保存起來,當下一次再訪問服務器時就把cookie再發送給服務器。cookie

cookie是由服務器端建立,而後經過響應發送給客戶端的一個鍵值對。客戶端會保存cookie,並會標註cookie的來源。當客戶端向服務器發出請求時會把全部這個服務器cookie的包含在請求中發送給服務器,這樣服務器就能夠識別客戶端了。
2session

讓咱們用代碼級別來看一下cookie長什麼樣子?

首先咱們須要新建立一個項目,而後設置路由規則:

urls.py

from app01 import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login),
]

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.


def login(request):
    return render(request, 'login.html')

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login頁面</title>
</head>
<body>
    <form action="" method="post">
        {% csrf_token %}
        用戶名 <input type="text" name="user">
        密碼 <input type="text" name="pwd">
        <input type="submit" value="submit">
    </form>
</body>
</html>

而後在model.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)

使用數據庫遷移命令來生成數據庫:

python3 manage.py makemigrations

python3 manage.py migrate

最後在數據庫中插入兩條記錄:
3

那麼當咱們把整個項目運行起來後,當咱們輸入用戶名和密碼若是正確以後,那麼就在瀏覽器設置一個cookie而後響應給客戶端,那麼咱們須要在views.py中去進行判斷了:

from django.shortcuts import render,HttpResponse,redirect
from app01.models import UserInfo
# Create your views here.


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('登錄成功')
            response.set_cookie({'is_login': True})
            return response
    
    return render(request, 'login.html')

此時我們在瀏覽器輸入正確的用戶和密碼後,讓咱們看下這次響應的內容:
4

此時的cookie已經放在響應體中了,當客戶端向此服務器發送請求的時候,就會攜帶上這個cookie,當咱們刷新此界面,就能夠看到了攜帶了此cookie:
5

那麼咱們能夠去模仿某網站了,若是你登錄過,那麼就進入index界面,若是沒有那就強制跳轉到登錄界面。首先咱們要寫一條路由規則:

urls.py

from django.contrib import admin
from django.urls import path
from app01 import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login),
    path('index/', views.index),
]

views.py

from django.shortcuts import render,HttpResponse,redirect
from app01.models import UserInfo
# Create your views here.


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('登錄成功')
            response.set_cookie('is_login', True)
            response.set_cookie('username', user.user)
            return response

    return render(request, 'login.html')


def index(request):
    is_login = request.COOKIES.get('is_login')
    if is_login:
        username = request.COOKIES.get('username')
        return render(request, 'index.html', locals())
    else:
        return redirect('/login/')

此時views.py中就是當用戶再login界面登錄後,系統會設置cookie將當前狀態和登錄用戶名記錄下來而後響應給客戶端,而後當用戶訪問index界面中,首先就去判斷用戶是否登錄,若是沒有登錄那麼就重定向到login頁面中,若是登錄則跳出歡迎界面。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h3>這是主界面</h3>
    <p>hello,{{ username }}</p>
</body>
</html>

那麼下圖就是瀏覽器訪問index所攜帶的cookie:
6

cookie源碼

class HttpResponseBase:
    def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
                   domain=None, secure=False, httponly=False, samesite=None):
        """
        Set a cookie.

        ``expires`` can be:
        - a string in the correct format,
        - a naive ``datetime.datetime`` object in UTC,
        - an aware ``datetime.datetime`` object in any time zone.
        If it is a ``datetime.datetime`` object then calculate ``max_age``.
        """
        self.cookies[key] = value
        if expires is not None:
            if isinstance(expires, datetime.datetime):
                if timezone.is_aware(expires):
                    expires = timezone.make_naive(expires, timezone.utc)
                delta = expires - expires.utcnow()
                # Add one second so the date matches exactly (a fraction of
                # time gets lost between converting to a timedelta and
                # then the date string).
                delta = delta + datetime.timedelta(seconds=1)
                # Just set max_age - the max_age logic will set expires.
                expires = None
                max_age = max(0, delta.days * 86400 + delta.seconds)
            else:
                self.cookies[key]['expires'] = expires
        else:
            self.cookies[key]['expires'] = ''
        if max_age is not None:
            self.cookies[key]['max-age'] = max_age
            # IE requires expires, so set it if hasn't been already.
            if not expires:
                self.cookies[key]['expires'] = http_date(time.time() + max_age)
        if path is not None:
            self.cookies[key]['path'] = path
        if domain is not None:
            self.cookies[key]['domain'] = domain
        if secure:
            self.cookies[key]['secure'] = True
        if httponly:
            self.cookies[key]['httponly'] = True
        if samesite:
            if samesite.lower() not in ('lax', 'strict'):
                raise ValueError('samesite must be "lax" or "strict".')
            self.cookies[key]['samesite'] = samesite

那麼經過這段源碼咱們能夠看出來除了響應體設置cookie由key-value參數,還有一些其餘的參數,例如超長時間max_age,cookie生效的路徑path等。

cookie超長時間

有時候咱們但願用戶在登錄某網站一段時間後cookie就過時,那麼咱們就能夠設置超長時間

views.py

from django.shortcuts import render,HttpResponse,redirect
from app01.models import UserInfo
# Create your views here.


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('登錄成功')
            response.set_cookie('is_login', True, max_age=20)  # 超長時間
            response.set_cookie('username', user.user)
            return response

    return render(request, 'login.html')


def index(request):
    is_login = request.COOKIES.get('is_login')
    if is_login:
        username = request.COOKIES.get('username')
        return render(request, 'index.html', locals())
    else:
        return redirect('/login/')

那麼當咱們用戶登錄login頁面後,再去訪問index界面,等待15秒鐘後會自動重定向到login界面。

cookie超長時間

expires默認None ,cookie失效的實際日期/時間。

這個和max_age不同的是這個要寫時間的字符串。

views.py

import datetime
date = datetime.datetime.strftime(year=2018, month=11, day=21, hour=3, minute=51, second=0)  # 東八區

response.set_cookie('is_login', True, expires=date)

那麼這樣寫的意思就是在2018年11月22日 上午11時51分00秒這個cookie失效。能夠自行去測試的,這個我就不演示了。

cookie生效路徑

cookie生效的路徑,瀏覽器只會把cookie回傳給帶有該路徑的頁面,這樣能夠避免將cookie傳給站點中的其餘應用。也就是說:若是個人index頁面須要cookie,那麼我就只須要在path後面設置爲此頁面就能夠了,其他的並不須要。

views.py

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('登錄成功')
            import datetime
            # date = datetime.datetime.strftime(year=2018, month=11, day=21, hour=3, minute=51, second=0)  # 東八區

            response.set_cookie('is_login', True, path='/login/')
            response.set_cookie('username', user.user)
            return response

    return render(request, 'login.html')

此時我們設置的路徑就是login頁面,爲了驗證效果,當咱們輸入正確的信息後,當再次輸入地址進入index界面中,由於cookie生效路徑沒有index,那麼就會自動跳轉到login頁面了。

本次暫不演示。

刪除cookie

response.delete_cookie("cookie_key",path="/",domain=name)

session

session是一個服務器端技術,利用這個技術,服務器在運行時能夠爲每個用戶的瀏覽器建立一個獨享的session對象,因爲session爲用戶瀏覽器獨享,因此用戶在訪問服務器web資源時,能夠把各自的數據存放在各自的session表中,當用戶再去訪問服務器中的其餘web資源時,其餘Web資源再從用戶各自的session中取出數據爲用戶服務。

爲何用session而不是cookie

session基於cookie實現的會話跟蹤,cookie存放在客戶端一旦丟失的話就會對用戶的數據構成威脅。

咱們來看一下cookie的保存:
7

當咱們輸入正確的帳號和密碼後,由服務器端響應體設置的cookie就會傳給客戶端,那麼客戶端再次請求的時候就會拿這個cookie去請求,由於是明文的、存放在瀏覽器的將變的十分不安全。

那麼讓咱們來看一下session怎麼作的?

首先設置兩條路由規則views.py:

path('login_session', views.login_session),
path('index_session', views.index_session),

同時設置視圖函數views.py

# session
def login_session(request):
    return render(request, 'login.html')


def index_session(request):
    return render(request, 'index.html')

那麼session是怎麼和cookie不一樣的呢?

views.py

def login_session(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:
            request.session['is_login'] = True
            request.session['username'] = user.user

            return HttpResponse('登錄成功')
    return render(request, 'login.html')

當咱們去瀏覽器訪問下能夠看到此時的sessionid:
8

在這裏不一樣的是session和cookie建立的步驟不一樣:

  1. 首先服務器端會生成隨機字符串123dfdf
  2. 而後使用語句建立sessionid:request.set_cookid('sessionid','123dfdf)
  3. 最後在django_session表中建立一條記錄:sessionid session_data

那麼到最後返回到客戶端的就是一個sessionid,當客戶端瀏覽器再請求服務器時,服務器就會根據這個sessionid在djano_session表中查找這麼一條記錄,咱們在建立數據庫的時候django_session表已經自動建立好了。
9

那麼此時我們設置一個視圖函數,看看session的數據是怎麼找到的:

views.py

def index_session(request):
    print(request.session.get('is_login'))
    is_login = request.session.get('is_login')
    if not is_login:
        return redirect('/login_session/')

    username = request.session.get('username')

    return render(request, 'index.html', locals())

session去找數據也是三個步驟,首先要確認是否是第一次訪問,若是是那麼就添加詞條記錄,若是不是那就更新操做:

  1. 首先找到sessionid
  2. 經過sessionid去django_session表中找到這條記錄
  3. 最後獲取到session_data

session的其餘方法

刪除session值:del request.session['username']

views.py

def index_session(request):

    print(request.session.get('is_login'))
    del request.session['username']
    is_login = request.session.get('is_login')
    if not is_login:
        return redirect('/login_session/')

    # username = request.session.get('username')

    return render(request, 'index.html', locals())

那麼此時我們訪問login_session後再訪問index_session的時候,此時的username被咱們刪掉了,而後此時刷新界面是這樣的:
10

flush():刪除當前的會話數據並刪除會話的Cookie。

logout.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h3>這是主界面</h3>
    <p>hello,{{ username }}</p>
    <a href="/logout/">註銷</a>
</body>
</html>

views.py

def logout(request):
    request.session.flush()

    return redirect('/login_session/')

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h3>這是主界面</h3>
    <p>hello,{{ username }}</p>
    <a href="/logout/">註銷</a>
</body>
</html>

此時點擊註銷標籤,此時就會刪除當前會話的cookie。

session的設置

django中默認支持session的,而且默認是將Session數據存儲在數據庫中,即:django_session 表中。

配置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,默認修改以後才保存(默認)

基於session上次登錄時間

views.py

def login_session(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:
            request.session['is_login'] = True
            request.session['username'] = user.user

            import datetime
            now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            request.session['time'] = now

            return HttpResponse('登錄成功')
    return render(request, 'login.html')

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h3>這是主界面</h3>
{#    <p>hello,{{ username }}</p>#}
    <p>上次登錄時間:{{ time }}</p>
    <a href="/logout/">註銷</a>
</body>
</html>

基於cookie也是相似的作法。

相關文章
相關標籤/搜索