Cookie和Session組件

Cookie和Session

爲何會有cookie和session?

因爲http協議是無狀態的 沒法記住用戶是誰,cookie主要是在瀏覽器上記錄客戶的狀態,session主要是用來在服務端記錄客戶的狀態。python

cookie

Cookie的由來

你們都知道HTTP協議是無狀態的。數據庫

無狀態的意思是每次請求都是獨立的,它的執行狀況和結果與前面的請求和以後的請求都無直接關係,它不會受前面的請求響應狀況直接影響,也不會直接影響後面的請求響應狀況。django

一句有意思的話來描述就是人生只如初見,對服務器來講,每次的請求都是全新的。json

狀態能夠理解爲客戶端和服務器在某次會話中產生的數據,那無狀態的就覺得這些數據不會被保留。會話中產生的數據又是咱們須要保存的,也就是說要「保持狀態」。所以Cookie就是在這樣一個場景下誕生。後端

什麼是Cookie

cookie是保存在客戶端瀏覽器上的鍵值對,是服務端設置在客戶端瀏覽器上的鍵值對,也就意味着瀏覽器其實能夠拒絕服務端的"命令",瀏覽器有權限禁止服務端寫入cookie,默認狀況下 瀏覽器都是直接讓服務端設置鍵值對,瀏覽器

Cookie具體指的是一段小信息,下次訪問服務器時瀏覽器會自動攜帶這些鍵值對,以便服務器提取有用信息。緩存

Cookie的原理

cookie的工做原理是:由服務器產生內容,瀏覽器收到請求後保存在本地;當瀏覽器再次訪問時,瀏覽器會自動帶上Cookie,這樣服務器就能經過Cookie的內容來判斷這個是「誰」了。安全

查看Cookie

咱們使用Chrome瀏覽器,打開開發者工具。服務器

img

Django中操做Cookie

因爲視圖函數返回的都是HttpResponse對象(render,rendict,HttpResponse),因此能夠操做該對象來操做,利用該對象來操做cookie。

return HttpResponse()

操做

obj = HttpResponse()
obj.set_cookie(key,value)#告訴瀏覽器在本地保存一個鍵值對
obj.set_cookie(key,value,max_age=5)#設置超時時間
return obj

基於cookie作用戶的登陸認證

  1. request.COOKIES.get(key) # 檢驗用戶是否登陸
  2. 如何保存用戶在沒有登陸以前想訪問的那個頁面的url,而後當用戶用戶名和密碼輸入正確點擊登陸以後再跳轉會去
  3. request.path_info # 只拿後綴
  4. request.get_full_path() # 後綴加get請求攜帶的參數
  5. 利用上面的方法在裝飾器中獲取用戶想要訪問的url
  6. 在跳轉到登陸頁面的url的時候 以get請求攜帶參數的方式將用戶想要訪問的url攜帶過去
  7. 在登陸的視圖函數中 對url進行判斷
    1. 用戶沒有登陸的狀況下訪問了一個必須登陸的頁面
    2. 用戶直接訪問的就是登陸頁面
def login(request):
    # print(request.path_info)  # 只拿url 不拿get請求攜帶的額參數
    # print(request.get_full_path())  # 都拿

    if request.method == "POST":
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'jason' and password == '123':
            old_path = request.GET.get('next')
            if old_path:
                # 保存用戶登陸狀態
                obj = redirect(old_path)
            else:
                obj = redirect('/home/')
            obj.set_cookie('name','jason')  # 讓客戶端瀏覽器 記錄一個鍵值對
            # obj.set_cookie('name','jason',max_age=5)  # 讓客戶端瀏覽器 記錄一個鍵值對
            return obj
    return render(request,'login.html')

裝飾器,對要訪問的頁面進行修飾

from functools import wraps
def login_auth(func):
    @wraps(func)  # 裝飾器修復技術,讓其更加完美,若是不加這個的話,函數內部會打印裝飾器內的註釋
    # 加上以後,打印的是被裝飾的函數
    def inner(request, *args, **kwargs):
        if request.COOKIES.get('name'):
            res = func(request, *args, **kwargs)
            print(res, 6)
            return res
        else:
            target_url = request.path_info
            print(target_url, 5)
            return redirect('/login/?next=%s' % target_url)
    return inner
@login_auth
def home(request):
    obj = HttpResponse('我是主頁只能登錄後才能進來')
    return obj
@login_auth
def index(request):
    return HttpResponse("ddd")

獲取Cookie

if request.COOKIES.get('name'):#獲取cookies
    res = func(request, *args, **kwargs)
    return res
request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

參數:

  • default: 默認值
  • salt: 加密鹽
  • max_age: 後臺控制過時時間

設置Cookie

obj = redirect('/home/')
obj.set_cookie('name', 'json')
return obj
rep = HttpResponse(...)
rep = render(request, ...)

rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密鹽', max_age=None, ...)

設置時間

obj = redirect('/home/')
obj.set_cookie('name', 'json',max_age=5)
return obj
max_age=None, 超時時間,秒

參數:

  • key, 鍵
  • value='', 值
  • max_age=None, 超時時間
  • expires=None, 超時時間(IE requires expires, so set it if hasn't been already.)
  • path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie能夠被任何url的頁面訪問
  • domain=None, Cookie生效的域名
  • secure=False, https傳輸
  • httponly=False 只能http協議傳輸,沒法被JavaScript獲取(不是絕對,底層抓包能夠獲取到也能夠被覆蓋)

刪除Cookie

@login_auth
def logout(request):
    obj = redirect('/login/')
    obj.delete_cookie('name')# 刪除用戶瀏覽器上以前設置的usercookie值
    return obj

Session

session是保存在服務器上的鍵值對,因爲cookie是將全部的關鍵性信息保存在客戶端瀏覽器上的 數據不是很安全

因此有了session:session就是將數據保存在服務端 只給客戶端瀏覽器一個隨機字符串

服務端記錄了 隨機字符串與真實數據的對應關係

Session的由來

Cookie雖然在必定程度上解決了「保持狀態」的需求,可是因爲Cookie自己最大支持4096字節,以及Cookie自己保存在客戶端,可能被攔截或竊取,所以就須要有一種新的東西,它能支持更多的字節,而且他保存在服務器,有較高的安全性。這就是Session。

問題來了,基於HTTP協議的無狀態特徵,服務器根本就不知道訪問者是「誰」。那麼上述的Cookie就起到橋接的做用。

咱們能夠給每一個客戶端的Cookie分配一個惟一的id,這樣用戶在訪問時,經過Cookie,服務器就知道來的人是「誰」。而後咱們再根據不一樣的Cookie的id,在服務器上保存一段時間的私密資料,如「帳號密碼」等等。

總結而言:Cookie彌補了HTTP無狀態的不足,讓服務器知道來的人是「誰」;可是Cookie以文本的形式保存在本地,自身安全性較差;因此咱們就經過Cookie識別不一樣的用戶,對應的在Session裏保存私密的信息以及超過4096字節的文本。

另外,上述所說的Cookie和Session實際上是共通性的東西,不限於語言和框架。

Django中Session相關方法

1572438988352

django session表是針對瀏覽器的,不一樣的瀏覽器來 纔會有不一樣的記錄

前期準備

首先要進行數據庫遷移命令python manage.py makemigrations

python manage.py migrate

,遷移完成以後會有django_session表,這個表用來存儲session

設置session

def set_session(request):
    request.session['username']="sky" ## 僅僅只會在內存產生一個緩存
    return HttpResponse("設置session")

設置session的時候發生了什麼?

1.django內部自動生成了隨機的字符串
2.在django_session表中存入數據,在服務端默認狀況下須要django_session表來存儲session信息(沒有執行數據庫遷移命令會報錯)django session默認的過時時間是14天
session_key          session_data         date
隨機字符串1              數據1              ...
隨機字符串2              數據2             ...
隨機字符串3              數據3             ...
3.將產生的隨機字符串發送給瀏覽器 讓瀏覽器保存到cookie中sessionid:隨機字符串

設置多個session

def set_session(request):
    request.session['username']="sky" ## 僅僅只會在內存產生一個緩存
    request.session['passion']="123"
    return HttpResponse("設置session")

在數據表中仍是一條數據,隨機字符串會發生改變

獲取session

def get_session(request):
    print(request.session.get('username'))
    return HttpResponse('獲取session')

獲取session發生了什麼?

1.瀏覽器發送cookie到django後端以後 django會自動獲取到cookie值
2.拿着隨機字符串去django_session表中比對 是否有對應的數據
3.不管有沒有數據 你均可以經過request.session.get()
1.沒有返回的結果就是None
2.有則返回隨機字符串所對應的真實數據

刪除session

刪除session
    request.session.delete()  # 只刪除服務端的session
                
    request.session.flush()  # 瀏覽器和服務端所有刪除

session設置超時時間

request.session.set_expiry(value多種配置)
                數字
                0 
                不寫
                時間格式
request.session.set_expiry(value)
    * 若是value是個整數,session會在些秒數後失效。
    * 若是value是個datatime或timedelta,session就會在這個時間後失效。
    * 若是value是0,用戶關閉瀏覽器session就會失效。
    * 若是value是None,session會依賴全局session失效策略。

其餘的session設置

# 獲取、設置、刪除Session中數據
request.session['k1']#不推薦使用
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在則不設置
del request.session['k1']


# 全部 鍵、值、鍵值對
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()

# 會話session的key
request.session.session_key

# 將全部Session失效日期小於當前日期的數據刪除
request.session.clear_expired()

# 檢查會話session的key在數據庫中是否存在
request.session.exists("session_key")

# 刪除當前會話的全部Session數據
request.session.delete()
  
# 刪除當前的會話數據並刪除會話的Cookie。
request.session.flush() 
    這用於確保前面的會話數據不能夠再次被用戶的瀏覽器訪問
    例如,django.contrib.auth.logout() 函數中就會調用它。

# 設置會話Session和Cookie的超時時間
request.session.set_expiry(value)
    * 若是value是個整數,session會在些秒數後失效。
    * 若是value是個datatime或timedelta,session就會在這個時間後失效。
    * 若是value是0,用戶關閉瀏覽器session就會失效。
    * 若是value是None,session會依賴全局session失效策略。

Session流程解析

img

Session版登錄驗證

from functools import wraps


def check_login(func):
    @wraps(func)
    def inner(request, *args, **kwargs):
        next_url = request.get_full_path()
        if request.session.get("user"):
            return func(request, *args, **kwargs)
        else:
            return redirect("/login/?next={}".format(next_url))
    return inner


def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        if user == "alex" and pwd == "alex1234":
            # 設置session
            request.session["user"] = user
            # 獲取跳到登錄頁面以前的URL
            next_url = request.GET.get("next")
            # 若是有,就跳轉回登錄以前的URL
            if next_url:
                return redirect(next_url)
            # 不然默認跳轉到index頁面
            else:
                return redirect("/index/")
    return render(request, "login.html")


@check_login
def logout(request):
    # 刪除全部當前請求相關的session
    request.session.delete()
    return redirect("/login/")


@check_login
def index(request):
    current_user = request.session.get("user", None)
    return render(request, "index.html", {"user": current_user})

Django中的Session配置

Django中默認支持Session,其內部提供了5種類型的Session供開發者使用。

直接設置在sessings中就行。

session能保存在那些地方?

session是保存在服務端的加鍵值對

能夠保存在:

  1. 數據庫
  2. 文件
  3. 緩存數據庫
  4. 後端有不少方法來保存session信息,並非只能保存在數據庫中。
1. 數據庫Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默認)

2. 緩存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的緩存別名(默認內存緩存,也能夠是memcache),此處別名依賴緩存的設置

3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 緩存文件路徑,若是爲None,則使用tempfile模塊獲取一個臨時地址tempfile.gettempdir() 

4. 緩存+數據庫
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

其餘公用設置項:
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,默認修改以後才保存(默認)

CBV中加裝飾器相關(裝飾器修復)

CBV實現的登陸視圖

class LoginView(View):

    def get(self, request):
        """
        處理GET請求
        """
        return render(request, 'login.html')

    def post(self, request):
        """
        處理POST請求 
        """
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if user == 'alex' and pwd == "alex1234":
            next_url = request.GET.get("next")
            # 生成隨機字符串
            # 寫瀏覽器cookie -> session_id: 隨機字符串
            # 寫到服務端session:
            # {
            #     "隨機字符串": {'user':'alex'}
            # }
            request.session['user'] = user
            if next_url:
                return redirect(next_url)
            else:
                return redirect('/index/')
        return render(request, 'login.html')

要在CBV視圖中使用咱們上面的check_login裝飾器,有如下三種方式:

from django.utils.decorators import method_decorator

1. 加在CBV視圖的get或post方法上

from django.utils.decorators import method_decorator


class HomeView(View):

    def dispatch(self, request, *args, **kwargs):
        return super(HomeView, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, "home.html")
    
    @method_decorator(check_login)
    def post(self, request):
        print("Home View POST method...")
        return redirect("/index/")

2. 加在dispatch方法上

from django.utils.decorators import method_decorator


class HomeView(View):

    @method_decorator(check_login)
    def dispatch(self, request, *args, **kwargs):
        return super(HomeView, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, "home.html")

    def post(self, request):
        print("Home View POST method...")
        return redirect("/index/")

由於CBV中首先執行的就是dispatch方法,因此這麼寫至關於給get和post方法都加上了登陸校驗。

3. 直接加在視圖類上,但method_decorator必須傳 name 關鍵字參數

若是get方法和post方法都須要登陸校驗的話就寫兩個裝飾器。

from django.utils.decorators import method_decorator

@method_decorator(check_login, name="get")
@method_decorator(check_login, name="post")
class HomeView(View):

    def dispatch(self, request, *args, **kwargs):
        return super(HomeView, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, "home.html")

    def post(self, request):
        print("Home View POST method...")
        return redirect("/index/")

補充

CSRF Token相關裝飾器在CBV只能加到dispatch方法上,或者加在視圖類上而後name參數指定爲dispatch方法。

備註:

  • csrf_protect,爲當前函數強制設置防跨站請求僞造功能,即使settings中沒有設置全局中間件。
  • csrf_exempt,取消當前函數防跨站請求僞造功能,即使settings中設置了全局中間件。
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator


class HomeView(View):

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(HomeView, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, "home.html")

    def post(self, request):
        print("Home View POST method...")
        return redirect("/index/")

或者

from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator


@method_decorator(csrf_exempt, name='dispatch')
class HomeView(View):
   
    def dispatch(self, request, *args, **kwargs):
        return super(HomeView, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, "home.html")

    def post(self, request):
        print("Home View POST method...")
        return redirect("/index/")
相關文章
相關標籤/搜索