中间件,csrf
目录
---process_request(self, request)
---process_response(self, request, response)
---process_view(self, request, view_func, view_args, view_kwargs)
---process_template_response(self, request, response)
---process_exception(self, request, exception)
-中间件
--介绍
中间件是Django的门卫(Django生命周期流程图),用于在全局范围内改变Django的输入和输出
官方介绍是:中间件是一个用来处理Django的请求和响应的框架级别的钩子,是一个轻量、低级别的插件系统,每个中间件负责一些特定的功能
1、请求来的时候需要经过中间件的层层逐次(正向)检查才能进入Django后端
2、请求走的时候需要经过中间件的层层逐次(反向)检查才能返回给浏览器
Django自带7个中间件,新建一个Django项目时,在settings.py中:
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', ]
MIDDLEWARE配置是一个有序列表, 列表中是一个个字符串,但其实是一个个类的导入
eg:
from django.middleware.security import SecurityMiddleware
-拓展:importlib模块 自动将用字符串表示的路径解构成from…import…导入模式 eg: import importlib path_str = 'myfile.test' path_obj = importlib.import_module(path_str) # ==> from myfile import test # 拿到的结果就是from myfile import test类似的module对象 缺陷:只能最小到.py文件名,不能深入到py文件里的类,变量等。。 -从django中间件领会编程思想 针对django中间件的注释/关掉注释的调用方法思想,以后我们在写项目时也可利用importlib模块实现这一功能,那这样以后我们想要关闭某功能就只需在配置文件中注释掉该功能导入路径字符串即可;而需调用时只需打开注释即可。 比如项目《基于Django中间件的编程思想》: notice文件夹是一个功能包,内含qq.py,email.py等功能文件,而包都需有一个__init__文件来调用。配置文件中配置好模块导入路径后,在__init__中书写后: import settings import importlib def send_msg(content): for module_str in settings.NOTICE_LIST: # 获取到导入路径和类名str 'notice.qq' ’QQ‘ # 以.从右开始分割,分割一次 module_path, class_str = module_str.rsplit('.', maxsplit=1) # 1 利用字符串导入模块 module_obj = importlib.import_module(module_path) # 2 利用反射获取类名 class_name = getattr(module_obj, class_str) # 3 生成类对象 obj = class_name() # 4 利用鸭子类型直接调用send方法 obj.send(content) 即可在start启动文件中快捷启动。之后若想要关闭email的功能,只需去配置文件中注释掉'notice.email.Email'即可。
--自定义中间件
除了新建项目时Django自带的7个中间件,Django还允许我们自定义中间件
通过查看初始的7个中间件的源码,发现每个类下都有共同的5个方法:
process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
其中process_request和process_response用的最多且最为重要
---方法
# 自定义中间件的方法步骤 1、在项目或应用下创建一个任意名称的目录 2、在该目录下创建一个.py文件 3、在该py文件内书写类(必须继承MiddlewareMixin),然后在该类下定义那5个方法 from django.utils.deprecation import MiddlewareMixin 4、将类的路径以字符串形式注册到settings.py的MIDDLEWARE列表中
# 自定义一个中间件,无论form表单还是Ajax提交过来的json格式数据,统一存放在process_request的request.data中 from django.utils.deprecation import MiddlewareMixin import json class MyJsonMiddleware(MiddlewareMixin): def process_request(self, request): try: request.data = json.loads(request.body) except Exception as e: requset.data = request.POST
---process_request(self, request)
1、参数request和视图函数中的request是一样的
2、请求来的时候需要执行每个中间件的process_request方法,执行顺序从上往下
3、如果某个中间件中没有定义process_request方法,直接跳过执行下一个
4、如果process_request方法返回了HttpResponse对象(3个),那么请求不再继续往后执行,而是原路返回
5、中间件的process_request方法是在执行视图函数前执行的
'''app01下的mymiddleware目录下的mymiddlewaretest.py中''' from django.utils.deprecation import MiddlewareMixin class MyMiddleware1(MiddlewareMixin): def process_request(self, request): print("MyMiddleware1里面的process_request") class MyMiddleware2(MiddlewareMixin): def process_request(self, request): print("MyMiddleware2里面的 process_request") '''在settings.py中的MIDDLEWARE中注册上述两个中间件''' MIDDLEWARE = [ ....... 'app01.mymiddleware.mymiddlewaretest.MyMiddleware1', # 自定义中间件1 'app01.mymiddleware.mymiddlewaretest.MyMiddleware2' # 自定义中间件2 ] '''访问一个视图,终端打印如下''' MyMiddleware1里面的process_request MyMiddleware2里面的 process_request app01中的test视图
---process_response(self, request, response)
1、响应走的时候需要经过并执行每个中间件里的process_response方法
response参数是返回的HttpResponse对象
2、该方法必须返回一个HttpResponse对象,默认返回对象就是response,也可以返回自己的
3、执行顺序是从下往上,若某个中间件没有该方法则跳过它执行下一个
from django.utils.deprecation import MiddlewareMixin class MyMiddleware1(MiddlewareMixin): def process_request(self, request): print("MyMiddleware1里面的process_request") def process_response(self, request, response): print("MyMiddleware1里面的process_response") return response class MyMiddleware2(MiddlewareMixin): def process_request(self, request): print("MyMiddleware2里面的process_request") def process_response(self, request, response): print("MyMiddleware2里面的process_response") return response '''访问一个视图,终端输出结果如下''' MyMiddleware2里面的process_request MyMiddleware1里面的process_request app01中的test视图 MyMiddleware1里面的process_response MyMiddleware2里面的process_response
如果当某个中间件的process_request方法有返回对象直接返回后,就会走同级别的process_response返回了,而不会走下面的中间件了
---process_view(self, request, view_func, view_args, view_kwargs)
1、在执行完process_request方法后执行视图函数前,会执行该方法
2、该方法有4个参数:
request是HttpResponse对象
view_func是Django即将使用的视图函数
view_args是传递给视图的位置参数的列表
view_kwargs是传递给视图的关键字参数的字典
3、执行顺序是从上往下顺序执行。返回None时,Django将继续处理这个请求,继续执行其它中间件的process_view方法;返回HttpResponse对象时,将不再执行视图函数,而是在同级别掉头执行process_response方法
from django.utils.deprecation import MiddlewareMixin class MyMiddleware1(MiddlewareMixin): def process_request(self, request): print("MyMiddleware1里面的process_request") def process_response(self, request, response): print("MyMiddleware1里面的process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("MyMiddleware1中的process_view") print(view_func, view_func.__name__) class MyMiddleware2(MiddlewareMixin): def process_request(self, request): print("MyMiddleware2里面的process_request") def process_response(self, request, response): print("MyMiddleware2里面的process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("MyMiddleware2中的process_view") print(view_func, view_func.__name__) '''访问一个视图,终端输出结果如下''' MyMiddleware1里面的process_request MyMiddleware2里面的process_request MyMiddleware1中的process_view <function test at 0x000001DE68317488> test MyMiddleware2中的process_view <function test at 0x000001DE68317488> test app01中的test视图 MyMiddleware2里面的process_response MyMiddleware1里面的process_response
---process_template_response(self, request, response)
返回的HttpResponse对象有render属性时才会触发,视图函数执行完后立即执行
顺序是从下往上
---process_exception(self, request, exception)
当视图函数出现异常的情况下触发。若返回None,则将异常交给下一个中间件的process_exception方法来处理;若返回HttpResponse对象,则从同级开始往上执行process_response方法,并返回给浏览器。
执行顺序从下往上
-CSRF校验
--简介
csrf(Cross-site request forgery)跨站请求伪造:网站在给用户返回一个具有提交数据功能的页面的时候会给这个页面加一个唯一标识。当这个页面朝后端发送post请求时,后端会先校验这个唯一标识,如果不对则直接拒绝(显示403 Forbidden页面),成功才正常执行后续
之前已经接触过一个csrf相关的中间件了'django.middleware.csrf.CsrfViewMiddleware'
我们一开始让把它注释掉,再提交post请求的时候,就不会被forbidden了
现在使用csrf就可以不再注释这个中间件了
--form表单符合csrf校验
<form action="" method='post'> {% csrf_token %} <!--只需加上这段代码--> <p>username:<input type='text' name='username'></p> <p>target_user:<input type='text' name='target_user'></p> <p>money:<input type='text' name='money'></p> <input type = 'submit'> </form>
只需要加上{% csrf_token %}后,每次请求后端都会发一个唯一标识,体现在页面上就是多了一个<input type="hidden" name="csrfmiddlewaretoken" value="RbzvDpgCyvKjBSMnMuZiTXfyGcYBVlU3MoI5P6uYtG07kob0W1gXU8unS7AI3HDM">且每次访问value=""都不一样
--Ajax符合csrf校验
三种方式:
{# 引入通用方法js文件(第三种方式) #} <script src="{% static 'js/mysetup.js'%}"></script> {# ajax请求代码 #} <script> $('#submit').click(function (){ $.ajax({ url:'', type:'post', {#// 第一种 利用标签查找到该随机字符串 data的键必须是csrfmiddlewaretoken#} {#data:{'name':'weer','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},#} {#// 第二种 利用模板语法快捷书写#} {#data:{'username':'weer', 'csrfmiddlewaretoken': '{{ csrf_token }}'},#} // 第三种 通用方式 引入官方js代码到html页面 然后啥也不写 success:function (args){ } }) }) </script>
function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); 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装饰器添加方法
现有如下需求:
1.网站整体都不校验csrf,就单单几个视图函数要校验
2.网站整体都校验csrf,就单单几个视图函数不校验
要实现上述需求,需要用到装饰器
from django.views.decorators.csrf import csrf_protect, csrf_exempt csrf_protect: 需要校验 csrf_exempt: 忽略校验
针对需求1:
注释掉那个csrf中间件,然后对要csrf校验的视图函数上加@csrf_protect
针对需求2:
打开那个csrf中间件,对不csrf校验的视图函数上加@csrf_exempt
---CBV方式添加csrf装饰器
from django.utils import method_decorator from django.views import View # @method_decorator(csrf_protect, name = 'post') # 针对csrf_protect,第二种方式可以 # @method_decorator(csrf_exempt, name = 'post') # 针对csrf_exempt,第二种方式不可以 @method_decorator(csrf_exempt, name = 'dispatch') # 针对csrf_exempt,这种可以(四) class MyCsrfToken(View): # @method_decorator(csrf_protect) # 针对csrf_protect,第三种方式可以 # @method_decorator(csrf_exempt) # 针对csrf_exempt,第三种方式可以 def dispatch(self, request, *args, **kwargs): return super(MyCsrfToken, self).dispatch(request, *args, **kwargs) def get(self, request): return HttpResponse('get') # @method_decorator(csrf_protect) # 针对csrf_protect,第一种方式可以 # @method_decorator(csrf_exempt) # 针对csrf_exempt,第一种方式不可以 def post(self, request): return HttpResponse('post')
总结:csrf_protect三种方式都行;csrf_exempt只有第三种方式或“四”可行