django中间件需要了解的方法 importlib模块 csrf校验策略 csrf相关装饰器

django中间件三个需要了解的方法

1.process_view
	路由匹配成功之后执行视图函数/类之前自动触发(顺序同process_request)
2.process_exception
	视图函数/类执行报错自动触发(顺序同process_response)
3.process_template_response
	视图函数/类返回的HttpResponse对象含有render并且对应一个方法的时候自动触发(顺序同process_response)

首先自定义中间件:
image-20221222091545593

定义完之后要对中间件进行注册

process_view

image-20221222092053234

process_view何时触发:
路由匹配成功之后,执行视图函数/类之前自动触发。(顺序同process_request,经过每一个中间件。)

执行顺序:

image-20221222091840440

process_exception

在类中添加此方法:
image-20221224144031367

process_exception何时触发:
如下图可见此方法在正常的一次请求流程中不会触发:image-20221222092344976

exception和异常相关。当执行视图函数的时候报错了,自动触发。
image-20221222092434485

注意是从下往上执行,process_exception的形参接受视图函数的报错信息。
image-20221222092514381

process_template_response

image-20221222092655626

注意:参数有response的都要使用return进行返回。

执行顺序:

image-20221222092717384

可发现在正常的一次请求流程中此方法没有执行。

process_template_responsed方法的执行条件:
视图函数、视图类返回的Httpresponse对象含有render属性,并且render属性对应一个render方法的时候自动触发。
image-20221222092824727

示例:
image-20221222092848171

基于django中间件实现功能的插拔式设计

将各个功能制作成配置文件的字符串形式
	如果想拥有该功能就编写对应的字符串
	如果不想有该功能则注释掉对应的字符串
 
补充知识	
	如果利用字符串导入模块
import importlib
s1 = 'bbb.b'  # aaa.bbb.ccc.b
res = importlib.import_module(s1)  # from aaa.bbb.ccc import b
print(res)  # <module 'bbb.b' from 'D:\\pythonProject03\\djangomiddle\\bbb\\b.py'>
'''注意字符串的结尾最小单位只能是py文件 不能是py文件里面的变量名'''
思考django中间件是如何处理的(最小单位是类名)


需求分析
	模拟编写一个消息通知功能(微信、qq、邮箱)
 
方式1:基于函数封装的版本
	没有眼前一亮的感觉 很一般
方式2:基于django中间件的功能设计
 	眼前一亮 回味无穷

django中间件中每个字符串都代表特定的功能。
这就叫功能的插拔式设计。image-20221222094646821

也就是说写字符串相当于导入了某个模块:
image-20221222095023331

正常情况下导模块需要用到import句式:
image-20221222095146882

示例:

image-20221222095218008

importlib模块

importlib模块支持传递字符串来导入模块。
如下使用importlib模块与使用import句式会获得相同的结果。

使用importlib模块:image-20221222095316813

使用from ... import ...句式:
image-20221222095335747

更多例子:

import importlib
import b
str_module = 'b'
res = importlib.import_module(str_module)
print(type(res))  # <class 'module'>
print(type(b))  # <class 'module'>

importlib模块底层原理:
将字符串按照点切割:
image-20221222095449882

importlib中的字符串的结尾最小单位只能到py文件名,而不能是py文件的的某个变量名。

如下图desc是b.py文件中的变量名:image-20221222095735092

需求分析:
编写一个消息通知功能(微信、qq、邮箱都会收到消息)。

方式1:基于函数封装

每个功能都写一个函数:

image-20221222100124727

封装总方法:
image-20221222100239455

方式2:基于django中间件的功能设计

创建配置文件settings.py、启动文件start.py。再创建三个功能文件放入notify目录。

image-20221222100610144

实现功能拆分,进行解耦合。

email功能文件下,写一个功能类:
image-20221222100705034

核心功能函数send_msg:

image-20221222100741007

不同的类,当有相同的功能时,他们的名字也应该相同(多态性):

image-20221222101009380

只要发消息,就使用send_msg方法。

编写settings.py:
image-20221222101249968

思考django中间件是如何处理的:

notify是一个包,在start.py导入包名notify:
image-20221222101453305

导入包名notify实际是导入的包中的__init__文件。

start.py文件中,执行settings定义的函数send_all:

image-20221222101631957

__init__文件中导入settings.py文件内的列表:

image-20221222101753207

for循环取出列表中的字符串,并使用rsplit方法做切分:
image-20221222101848005

得到用字符串表示模块路径、模块中的类名:

image-20221222101904293

使用importlib模块的功能,通过字符串获得模块类型:

image-20221222102006200

由于模块也是对象,通过反射从模块中拿名字:(也就是拿到真正的类)
image-20221222102148963

类名加括号产生对象:
image-20221222102231397

这个对象里面就有send_msg方法了,调用send_msg传入参数,实现功能。

csrf跨站请求伪造

钓鱼网站:模仿一个正规的网站 让用户在该网站上做操作 但是操作的结果会影响到用户正常的网站账户 但是其中有一些猫腻
	eg:英语四六级考试需要网上先缴费 但是你会发现卡里的钱扣了但是却交到了一个莫名其妙的账户 并不是真正的四六级官方账户
        
模拟钓鱼网站案例:转账案例
	内部隐藏标签
 
思考:作为网站的开发者如何区分真假网站页面发送的请求

img

了解更多:https://blog.csdn.net/qq_45803593/article/details/124727762

csrf校验策略

我们的网站无法识别哪些请求自己内部的页面发送过来的,哪些请求是钓鱼网站发过来的。
解决办法:在提交数据的位置添加唯一标识

1.form表单csrf策略

form表单内部添加 {% csrf_token %}

示例:
image-20221225164438013

查看表单的变化:

image-20221225164638574

每次刷新时这个value值都会变化。后端通过这个唯一标识,来区分真网站和钓鱼网站。

2.ajax请求csrf策略

csrf会导致ajax请求无法发送。

点击按钮发送ajax请求:
image-20221225165249155

无法通过crsf校验:
image-20221225165622419

你只要朝服务端发请求,请求里就必须含有crsf相关的键值对。

方式1:标签查找

data:{'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},

通过标签查找,获取{% csrf_token %}产生的标签的value值:

image-20221225170021965

方式2:利用模板语法自动获取

   data:{ 'csrfmiddlewaretoken':'{{ csrf_token }}','username':'jason'},
    
'''注意:一定要用引号引起来'''

示例:

image-20221225170504818

方式3:引入官网js脚本

当前后端分离时,没有模板语法,所以可以使用官网js脚本。
在django静态文件里,添加如下js脚本:

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
  beforeSend: function (xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", csrftoken);
    }
  }
});

在页面上把这个静态文件加载过来即可(脚本名字可以自定义):
image-20221225171528059

csrf相关装饰器

在使用csrf校验时,我们可能会遇到如下两种需求:

整个django项目都校验csrf 但是某些个视图函数\类不想校验
整个django项目都不校验csrf 但是某些个视图函数\类需要校验

这时候可以给视图函数、视图类添加装饰器来实现需求。

FBV添加装饰器的方式

与正常函数添加装饰器一致。

from django.views.decorators.csrf import csrf_exempt, csrf_protect
# @csrf_exempt
@csrf_protect
def transfer_func(request):pass

csrf_protect装饰的函数,添加crsf校验。
csrf_exempt装饰的函数,免除crsf校验。

CBV添加装饰器的方式

与正常情况不一样 需要注意。需要额外导入method_decorator
主要有三种方式:

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

# @method_decorator(csrf_protect, name='post')  # 方式2:单独生效
class MyView(views.View):
    @method_decorator(csrf_protect)  # 方式3:整个类中生效
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    # @method_decorator(csrf_protect)  # 方式1:单独生效
    def post(self, request):
        return HttpResponse('from cbv post view')
  
 CBV添加装饰器时,注意有一个装饰器是特例只能有一种添加方式>>>:csrf_exempt
 	只有在dispatch方法添加才会生效

方式1:装饰类中方法

对被装饰的方法单独生效。
image-20221225174045232

这里表示对post请求添加csrf校验。

方式2:装饰整个类

装饰整个类时,需要传入参数,申明装饰的是类中哪个方法,是需要添加csrf校验,还是免除csrf校验。

image-20221225174349013

这里还是对post请求添加csrf校验。

方式3:装饰dispatch方法

我们在自己的视图类中,写一个dispatch方法,再使用super调用父类的dispatch,最后给dispatch方法添加装饰器。
装饰dispatch方法时,会对类中所有方法生效。

image-20221225174801064

这里表示对视图类中所有方法都添加csrf校验。

特例 csrf_exempt

在全局crsf校验打开的情况下:我们想让我们的CBV不校验crsf。

image-20221225175801997

如下图所示:csrf_exempt只有在dispatch方法添加才会生效

image-20221225175708635

posted @ 2022-12-24 22:34  passion2021  阅读(49)  评论(0编辑  收藏  举报