• 16:django 有条件的视图处理(Last-Modified和ETag)&&加密签名


    有条件的视图处理

    上一节我们介绍了缓存来减轻服务器的负担,这里的有条件的视图处理也从一定程度上减轻了服务器的负担,在正式介绍之前,先来看两个概念:Last-Modified和ETag

    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 无法解决的一些问题。
    1、一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
    2、某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒)
    3、某些服务器不能精确的得到文件的最后修改时间;
    为此,HTTP/1.1引入了 Etag(Entity Tags).Etag仅仅是一个和文件相关的标记,可以是一个版本标记,比如说v1.0.0或者说"2e681a-6-5d044840"这么一串看起来很神秘的编码。但是HTTP/1.1标准并没有规定Etag的内容是什么或者说要怎么实现,唯一规定的是Etag需要放在""内。

    知道了这两个概念之后,我们应该知道这两个概念的作用了吧,也大概知道我们接下来要讲解的是什么啦。没错,就是,django中如何使用Last-Modified和ETag这两个概念呢

    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应用安全的黄金法则是:永远不要相信从不受信任来源的数据。但有时候我们只能从不受信任的媒介获取或者发送数据,或者不受信任的媒介更具吸引力,比如我们确保我们的数据一定是安全的,并且不受信任的媒介比受信任媒介要快捷甚至便宜的多。加密签名的值一旦被篡改就会被检测到,这是我们能确保数据安全的共识。下面是几个加密签名应用的例子:

    • 找回密码所用的url
    • 确保form表单中的隐藏域没有被修改
    • 一次性的允许访问保护数据的加密url

    django提供了一个底层的用来加密数据的API和一个高层的用来设置和读取加密cookies的API

    保护好你的SECRET_KEY

    用django-admin.py startproject命令生成的项目自带一个自动随机生成的SECRET_KEY,注意不要泄露这个key

    使用低层次的API

    django的加密方法源代码位于djangocore目录下的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'}
  • 相关阅读:
    创建与合并分支
    Git丢弃本地修改
    《人月神话》小记
    财商培养
    赚钱有道,增加睡后收入
    学点经济学,升级认知
    保险小白普及知识
    管理决策、资源分配的最理想状态
    AI时代做一个终身学习者
    基于需求的测试
  • 原文地址:https://www.cnblogs.com/qwj-sysu/p/4215144.html
Copyright © 2020-2023  润新知