Django 中间件

中间件

中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。

他在setting.py中默认有7个,每一个中间件都有具体的功能。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

中间件的方法

中间件一共4个方法

process_request

process_view

process_exception

process_response

process_request,process_response

中间件自定义使用

自定义中间件的步骤

  1. 必须继承MiddlewareMixin,需要导入from django.utils.deprecation import MiddlewareMixin

  2. 注意在setting的MIDDLEWARE中加入这个自定义的中间件
    注册的格式为应用名.中间件文件名.类名

  3. 如果中间件要处理请求,也要处理响应,就至少要process_request和process_response两个函数

    2.1 process_request要传request,没有return

    2.2 process_response传request和response,必须要有return

代码示例

视图:

def session_log(request):
    print("session_log视图运行")
    return render(request)

自定义中间件

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Md1(MiddlewareMixin):
    def process_request(self,reuqest):
    	print("自定义的中间件--request---from Md1")
        #return HttpResponse("Md2中断")
    def process_response(self,request,response):
        print("自定义的中间件--response--from Md1")
        return response
           
class Md2(MiddlewareMixin):
    def process_request(self,reuqest):
    	print("自定义的中间件--request---from Md2")
    def process_response(self,request,response):
        print("自定义的中间件--response--from Md2")
        return response

输出结果

自定义的中间件--request---from Md1
自定义的中间件--request---from Md2
session_log视图运行
自定义的中间件--response--from Md2
自定义的中间件--response--from Md1

注意

如果当请求到达请求2的时候直接不符合条件返回,即return HttpResponse("Md2中断"),程序将把请求直接发给中间件2返回,然后依次返回到请求者,结果如下:

返回Md2中断的页面,后台打印如下,直接在中间件1就返回了,并不会执行到后续的中间件去:

自定义的中间件--request---from Md1
自定义的中间件--response--from Md1

可以看到视图session_log没有执行.并且直接返回了

中间件此时的请求流程

中间件添加登录认证

class Md3(MiddlewareMixin):
    def process_request(self, request):
        pass_list=["/home/index/","/home/login/"]
        if request.path in pass_list: # 对于pass_list清单内的url可以不用进行登录就能访问
            return None					# 返回None直接做放行处置
        if not request.user.username: # 对于其他站点全局要求登录后才能访问
            return render("/home/login/")
        
        # return HttpResponse("Md2中断")

    def process_response(self, request, response):
        print("Md2返回")
        return response

process_view

process_view(self,request,callback,callbakc_args,callback_kwargs)

代码示例

视图函数

def session_log(request):
    print("session_log视图运行")
    form_reg_data = Form_Reg()  # 用django的form组件在get请求下渲染网页
    return render(request, "app02_login.html", locals())

中间件代码示例

class Md1(MiddlewareMixin):
    def process_request(self, reuqest):
        print("自定义的中间件--request---from Md1")
        # return HttpResponse("Md1中断")
    def process_response(self, request, response):
        print("自定义的中间件--response--from Md1")
        return response

    def process_view(self,request,callback,callback_args,callback_kwargs):
        print("from Md1 函数名字为>>>%s"%callback.__name__)

        print("运行的视图函数是from Md1")


class Md2(MiddlewareMixin):
    def process_request(self, reuqest):
        print("自定义的中间件--request---from Md2")

    def process_response(self, request, response):
        print("自定义的中间件--response--from Md2")
        return response

    def process_view(self,request,callback,callback_args,callback_kwargs):
        print("from Md2 函数名字为>>>%s" % callback.__name__)
        print("运行的视图函数是  from Md2")

运行结果

自定义的中间件--request---from Md1
自定义的中间件--request---from Md2
from Md1 函数名字为>>>session_log
运行的视图函数是from Md1
from Md2 函数名字为>>>session_log
运行的视图函数是  from Md2
session_log视图运行
自定义的中间件--response--from Md2
自定义的中间件--response--from Md1

我们可以看到中间的运行顺序分别为两个process_request,然后是process_view,然后才是视图函数的运行.

再从原始路径后入先出进行返回.

运行示意图

中间件安全通用配置

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class SecurityMiddleware(MiddlewareMixin):

    def process_request(self, request):
        # 这里可以处理请求级别的安全措施,例如:
        # 确保使用 HTTPS
        if not request.is_secure():
            return HttpResponse("Please use HTTPS.", status=403)

    def process_response(self, request, response):
        # 设置 HTTP 响应头以提高安全性
        response["Access-Control-Allow-Origin"] = "http://127.0.0.1:4100"
        response["Access-Control-Allow-Methods"] = "DELETE,GET,POST,PATCH"
        if request.method == "OPTIONS":
            # 允许任意的请求头
            # X-Custom-Header 来确定实际的跨域请求是否被服务器允许 ,如果要区分options请求则必须代这个参数
            # Content-Type,Authorization表示正常请求需要者2个参数
            response["Access-Control-Allow-Headers"] = "Content-Type,Authorization,X-Custom-Header"
            # response["Access-Control-Allow-Headers"] = "*"
            # 在接下来的3600秒内,允许跳过预检请求 直接发送实际的请求 减少跨域请求所需的预检请求数量,从而减少了网络流量和延迟。
            response["Access-Control-Max-Age"] = "3600"
            return response
        # 任意网址
        # # 允许任意的请求头
        response["Access-Control-Allow-Headers"] = "Content-Type,Authorization"
        # 设置强缓存,3600时间范围内设置私有缓存
        # private(默认): 只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器, 若有max-age, 则缓存期间不访问服务器.
        # must-revalidate 是HTTP缓存头部中的一个指令(directive),它是指明了客户端必须在缓存过期时向服务器验证缓存的有效性。避免使用过期的缓存。这对于需要及时更新的内容非常重要,
        # 详情Cache-Control属性可以了解 https://www.cnblogs.com/JackieDYH/p/17634686.html
        response['Cache-Control'] = 'max-age=3600, private, must-revalidate'


        # 1. X-Content-Type-Options: nosniff
        # 不允许浏览器进行 MIME 嗅探,强制使用服务器定义的 Content-Type
        response['X-Content-Type-Options'] = 'nosniff'

        # 2. Content Security Policy (CSP) 原理还是同源策略,会影响加载css js的外部链接 慎用
        # 限制资源(如脚本、样式和图片)的加载来源,减少 XSS 攻击风险
        response['Content-Security-Policy'] = (
            "default-src 'self'; "
            "script-src 'self'; "
            "style-src 'self'; "
            "img-src 'self' data:; "
            "frame-ancestors 'none';"
        )

        # 3. X-Frame-Options
        # 防止点击劫持攻击,禁止将页面嵌入到其他站点的 iframe 中
        response['X-Frame-Options'] = 'DENY'

        # 4. HTTP Strict Transport Security (HSTS) 前提是配置了https证书
        # 告诉浏览器在一定时间内仅通过 HTTPS 访问
        response['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'

        # 5. Referrer-Policy
        # 控制 HTTP Referer 头的值,防止泄露敏感信息
        response['Referrer-Policy'] = 'strict-origin-when-cross-origin'

        # 6. X-XSS-Protection  部分浏览器支持 比如edge 微软的IE
        # 启用浏览器的 XSS 防护机制
        response['X-XSS-Protection'] = '1; mode=block'

        return response

注意:

process_view如果有返回值的话,会跳过后续的process_view以及所有的视图函数,但是所有的process_response还会继续执行

视图函数示例

def session_log(request):
    print("session_log视图运行")
    form_reg_data = Form_Reg()  # 用django的form组件在get请求下渲染网页
    return render(request, "app02_login.html", locals())

中间件代码示例

class Md1(MiddlewareMixin):
    def process_request(self, reuqest):
        print("自定义的中间件--request---from Md1")
        # return HttpResponse("Md1中断")
    def process_response(self, request, response):
        print("自定义的中间件--response--from Md1")
        return response

    def process_view(self,request,callback,callback_args,callback_kwargs):
        print("from Md1 函数名字为>>>%s"%callback.__name__)
        ret = callback(request,*callback_args,**callback_kwargs)
        return ret
        print("运行的视图函数是  from Md1")


class Md2(MiddlewareMixin):
    def process_request(self, reuqest):
        print("自定义的中间件--request---from Md2")

    def process_response(self, request, response):
        print("自定义的中间件--response--from Md2")
        return response

    def process_view(self,request,callback,callback_args,callback_kwargs):
        print("from Md2 函数名字为>>>%s" % callback.__name__)
        ret = callback(request, *callback_args, **callback_kwargs)
        print("运行的视图函数是  from Md2")
        

执行结果:

自定义的中间件--request---from Md1
自定义的中间件--request---from Md2
from Md1 函数名字为>>>session_log
#这一步执行的是中间件的process_view中的ret = callback(request,*callback_args,**callback_kwargs)
session_log视图运行
# 可以看到Md2中的process_view中的打印输出没有执行,直接进行process_response
自定义的中间件--response--from Md2
自定义的中间件--response--from Md1

process_exception

process_exception(self,request,exception

当view视图出现错误时候,会自动走中间件的process_exception,再走process_response

流程图如下:

posted @ 2021-09-29 14:53  零哭谷  阅读(39)  评论(0)    收藏  举报