django中间件需要了解的方法 importlib模块 csrf校验策略 csrf相关装饰器
django中间件三个需要了解的方法
1.process_view
路由匹配成功之后执行视图函数/类之前自动触发(顺序同process_request)
2.process_exception
视图函数/类执行报错自动触发(顺序同process_response)
3.process_template_response
视图函数/类返回的HttpResponse对象含有render并且对应一个方法的时候自动触发(顺序同process_response)
首先自定义中间件:
定义完之后要对中间件进行注册。
process_view
process_view何时触发:
路由匹配成功之后,执行视图函数/类之前自动触发。(顺序同process_request,经过每一个中间件。)
执行顺序:
process_exception
在类中添加此方法:
process_exception何时触发:
如下图可见此方法在正常的一次请求流程中不会触发:
exception和异常相关。当执行视图函数的时候报错了,自动触发。
注意是从下往上执行,process_exception的形参接受视图函数的报错信息。
process_template_response
注意:参数有response的都要使用return进行返回。
执行顺序:
可发现在正常的一次请求流程中此方法没有执行。
process_template_responsed方法的执行条件:
视图函数、视图类返回的Httpresponse对象含有render属性,并且render属性对应一个render方法的时候自动触发。
示例:
基于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中间件中每个字符串都代表特定的功能。
这就叫功能的插拔式设计。
也就是说写字符串相当于导入了某个模块:
正常情况下导模块需要用到import句式:
示例:
importlib模块
importlib模块支持传递字符串来导入模块。
如下使用importlib模块与使用import句式会获得相同的结果。
使用importlib模块:
使用from ... import ...
句式:
更多例子:
import importlib
import b
str_module = 'b'
res = importlib.import_module(str_module)
print(type(res)) # <class 'module'>
print(type(b)) # <class 'module'>
importlib模块底层原理:
将字符串按照点切割:
importlib中的字符串的结尾最小单位只能到py文件名,而不能是py文件的的某个变量名。
如下图desc是b.py文件中的变量名:
需求分析:
编写一个消息通知功能(微信、qq、邮箱都会收到消息)。
方式1:基于函数封装
每个功能都写一个函数:
![image-20221222100124727](https://img2023.cnblogs.com/blog/2614258/202212/2614258-20221224223013535-1215829821.png)
封装总方法:
方式2:基于django中间件的功能设计
创建配置文件settings.py、启动文件start.py。再创建三个功能文件放入notify目录。
实现功能拆分,进行解耦合。
email功能文件下,写一个功能类:
核心功能函数send_msg:
不同的类,当有相同的功能时,他们的名字也应该相同(多态性):
只要发消息,就使用send_msg方法。
编写settings.py:
思考django中间件是如何处理的:
notify是一个包,在start.py导入包名notify:
导入包名notify实际是导入的包中的__init__
文件。
start.py文件中,执行settings定义的函数send_all:
在__init__
文件中导入settings.py文件内的列表:
for循环取出列表中的字符串,并使用rsplit方法做切分:
得到用字符串表示模块路径、模块中的类名:
使用importlib模块的功能,通过字符串获得模块类型:
由于模块也是对象,通过反射从模块中拿名字:(也就是拿到真正的类)
类名加括号产生对象:
这个对象里面就有send_msg方法了,调用send_msg传入参数,实现功能。
csrf跨站请求伪造
钓鱼网站:模仿一个正规的网站 让用户在该网站上做操作 但是操作的结果会影响到用户正常的网站账户 但是其中有一些猫腻
eg:英语四六级考试需要网上先缴费 但是你会发现卡里的钱扣了但是却交到了一个莫名其妙的账户 并不是真正的四六级官方账户
模拟钓鱼网站案例:转账案例
内部隐藏标签
思考:作为网站的开发者如何区分真假网站页面发送的请求
了解更多:https://blog.csdn.net/qq_45803593/article/details/124727762
csrf校验策略
我们的网站无法识别哪些请求自己内部的页面发送过来的,哪些请求是钓鱼网站发过来的。
解决办法:在提交数据的位置添加唯一标识
1.form表单csrf策略
form表单内部添加 {% csrf_token %}
示例:
查看表单的变化:
每次刷新时这个value值都会变化。后端通过这个唯一标识,来区分真网站和钓鱼网站。
2.ajax请求csrf策略
csrf会导致ajax请求无法发送。
点击按钮发送ajax请求:
无法通过crsf校验:
你只要朝服务端发请求,请求里就必须含有crsf相关的键值对。
方式1:标签查找
data:{'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},
通过标签查找,获取{% csrf_token %}
产生的标签的value值:
方式2:利用模板语法自动获取
data:{ 'csrfmiddlewaretoken':'{{ csrf_token }}','username':'jason'},
'''注意:一定要用引号引起来'''
示例:
方式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);
}
}
});
在页面上把这个静态文件加载过来即可(脚本名字可以自定义):
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:装饰类中方法
对被装饰的方法单独生效。
这里表示对post请求添加csrf校验。
方式2:装饰整个类
装饰整个类时,需要传入参数,申明装饰的是类中哪个方法,是需要添加csrf校验,还是免除csrf校验。
这里还是对post请求添加csrf校验。
方式3:装饰dispatch方法
我们在自己的视图类中,写一个dispatch方法,再使用super调用父类的dispatch,最后给dispatch方法添加装饰器。
装饰dispatch方法时,会对类中所有方法生效。
这里表示对视图类中所有方法都添加csrf校验。
特例 csrf_exempt
在全局crsf校验打开的情况下:我们想让我们的CBV不校验crsf。
如下图所示:csrf_exempt只有在dispatch方法添加才会生效