16:django 有條件的視圖處理(Last-Modified和ETag)&&加密簽名

有條件的視圖處理

上一節咱們介紹了緩存來減輕服務器的負擔,這裏的有條件的視圖處理也從必定程度上減輕了服務器的負擔,在正式介紹以前,先來看兩個概念:Last-Modified和ETagweb

Last-Modified

在瀏覽器第一次請求某一個URL時,服務器端的返回狀態會是200,內容是客戶端請求的資源,同時有一個Last-Modified的屬性標記此文件在服務期端最後被修改的時間,格式相似這樣:
Last-Modified : Fri , 12 May 2006 18:53:33 GMT
客戶端第二次請求此URL時,根據HTTP協議的規定,瀏覽器會向服務器傳送If-Modified-Since報頭,詢問該時間以後文件是否有被修改過:
If-Modified-Since : Fri , 12 May 2006 18:53:33 GMT
若是服務器端的資源沒有變化,則自動返回 HTTP 304(Not Changed.)狀態碼,內容爲空,這樣就節省了傳輸數據量。當服務器端代碼發生改變或者重啓服務器時,則從新發出資源,返回和第一次請求時相似。從而保證不向客戶端重複發出資源,也保證當服務器有變化時,客戶端可以獲得最新的資源。

ETag

請求流程

Etag由服務器端生成,客戶端經過If-Match或者說If-None-Match這個條件判斷請求來驗證資源是否修改。常見的是使用If-None-Match.請求一個文件的流程可能以下:
====第一次請求===
1.客戶端發起 HTTP GET 請求一個文件;
2.服務器處理請求,返回文件內容和一堆Header,固然包括Etag(例如"2e681a-6-5d044840")(假設服務器支持Etag生成和已經開啓了Etag).狀態碼200
====第二次請求===
1.客戶端發起 HTTP GET 請求一個文件,注意這個時候客戶端同時發送一個If-None-Match頭,這個頭的內容就是第一次請求時服務器返回的Etag:2e681a-6-5d044840
2.服務器判斷髮送過來的Etag和計算出來的Etag匹配,所以If-None-Match爲False,不返回200,返回304,客戶端繼續使用本地緩存;
流程很簡單,問題是,若是服務器又設置了Cache-Control:max-age和Expires呢,怎麼辦?
答案是同時使用,也就是說在徹底匹配If-Modified-Since和If-None-Match即檢查完修改時間和Etag以後,服務器才能返回304.(不要陷入到底使用誰的問題怪圈)

做用

Etag 主要爲了解決 Last-Modified 沒法解決的一些問題。
一、一些文件也許會週期性的更改,可是他的內容並不改變(僅僅改變的修改時間),這個時候咱們並不但願客戶端認爲這個文件被修改了,而從新GET;
二、某些文件修改很是頻繁,好比在秒如下的時間內進行修改,(比方說1s內修改了N次),If-Modified-Since能檢查到的粒度是s級的,這種修改沒法判斷(或者說UNIX記錄MTIME只能精確到秒)
三、某些服務器不能精確的獲得文件的最後修改時間;
爲此,HTTP/1.1引入了 Etag(Entity Tags).Etag僅僅是一個和文件相關的標記,能夠是一個版本標記,好比說v1.0.0或者說"2e681a-6-5d044840"這麼一串看起來很神祕的編碼。可是HTTP/1.1標準並無規定Etag的內容是什麼或者說要怎麼實現,惟一規定的是Etag須要放在""內。

 

知道了這兩個概念以後,咱們應該知道這兩個概念的做用了吧,也大概知道咱們接下來要講解的是什麼啦。沒錯,就是,django中如何使用Last-Modified和ETag這兩個概念呢django

condition裝飾器

Last-Modified和ETag在django中是兩個函數,前者返回一個日期類型數據,後者返回一個值(ETag值),這兩個函數能夠做爲參數傳遞給django.views.decorators.http.condition這個裝飾器,裝飾器原型是瀏覽器

condition(etag_func=None, last_modified_func=None)

這是一個使用的簡單例子緩存

def latest_entry(request, blog_id):
    return Entry.objects.filter(blog=blog_id).latest("published").published
from django.views.decorators.http import condition
@condition(last_modified_func=latest_entry)
def front_page(request, blog_id):
    ...

在django.views.decorators.http.condition文件裏還提供了另外兩個裝飾器,可使得只須要提供一個參數便可,看源碼很容易懂:安全

def etag(etag_func):
    return condition(etag_func=etag_func)

def last_modified(last_modified_func):
    return condition(last_modified_func=last_modified_func)

說完了Last-Modified和ETag,咱們繼續說一下服務器

django加密簽名

web應用安全的黃金法則是:永遠不要相信從不受信任來源的數據。但有時候咱們只能從不受信任的媒介獲取或者發送數據,或者不受信任的媒介更具吸引力,好比咱們確保咱們的數據必定是安全的,而且不受信任的媒介比受信任媒介要快捷甚至便宜的多。加密簽名的值一旦被篡改就會被檢測到,這是咱們能確保數據安全的共識。下面是幾個加密簽名應用的例子:cookie

  • 找回密碼所用的url
  • 確保form表單中的隱藏域沒有被修改
  • 一次性的容許訪問保護數據的加密url

django提供了一個底層的用來加密數據的API和一個高層的用來設置和讀取加密cookies的API數據結構

保護好你的SECRET_KEY

用django-admin.py startproject命令生成的項目自帶一個自動隨機生成的SECRET_KEY,注意不要泄露這個key函數

使用低層次的API

django的加密方法源代碼位於django\core目錄下的signing.py文件,能夠具體去看看,下面是一些基本的用法:編碼

>>> from django.conf import settings
>>> settings.configure()
>>> from django.core.signing import Signer
>>> signer = Signer()
>>> value = signer.sign("qiweijie")
>>> value
'qiweijie:lVrPb11e1K9K_DcxnMGNYk8t2aQ'
>>> original = signer.unsign(value)
>>> original
u'qiweijie'

使用「調味劑」參數(salt)

salt是「鹽,調味劑」的意思,若是你不想每次對同一字符加密的結果都同樣,那麼你能夠是用調味劑參數調劑一下,哈哈,看示例:

>>> signer = Signer()
>>> signer.sign('My string')
'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w'
>>> signer = Signer(salt='extra')
>>> signer.sign('My string')
'My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw'
>>> signer.unsign('My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw')
u'My string'

驗證時間戳

TimestampSigner是Signer的子類,添加了一個加密的時間戳給值。這容許你能夠確保一個加密的數據是在一個給定的時間內建立的

>>> from django.core.signing import TimestampSigner
>>> signer = TimestampSigner()
>>> value = signer.sign('hello')
>>> value
'hello:1NMg5H:oPVuCqlJWmChm1rA2lyTUtelC-c'
>>> signer.unsign(value)
u'hello'
>>> signer.unsign(value, max_age=10)
...
SignatureExpired: Signature age 15.5289158821 > 10 seconds
>>> signer.unsign(value, max_age=20)
u'hello'

保護複雜的數據結構

列表,元組和字典,若是你直接使用上述的方法,最後獲得的是一個字符串而不是原來的數據類型:

>>> dic = {1:2}
>>> sd=signer.sign(dic)
>>> signer.unsign(sd)
u'{1: 2}'

若是你想保護這些數據類型,請使用dumps和loads方法,它們都位於django.core.signing模塊裏面

dumps( objkey=Nonesalt='django.core.signing'compress=False)
loads( stringkey=Nonesalt='django.core.signing'max_age=None)
>>> from django.core import signing
>>> value = signing.dumps({"foo": "bar"})
>>> value
'eyJmb28iOiJiYXIifQ:1NMg1b:zGcDE4-TCkaeGzLeW9UQwZesciI'
>>> signing.loads(value)
{'foo': 'bar'}
相關文章
相關標籤/搜索