简单介绍:
django自带七个中间件,每个中间件都有各自对应的功能,并且django还支持程序员自定义中间件
在用django开发项目的项目的时候,只要是涉及到全局相关的功能都可以使用中间件方便的完成
# django中间件是django的门户1.请求来的时候需要先经过中间件才能到达真正的django后端2.响应走的时候最后也需要经过中间件才能发送出去django自带七个中间件
Django中间件:默认自带七个如下
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',]
解释,这其中的字符串元素是路径名,利用字符串导模块(利用importlib模块)
from django.middleware.security import SecurityMiddleware
查看几个中间件源码
class SessionMiddleware(MiddlewareMixin):def process_request(self, request):session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)request.session = self.SessionStore(session_key)def process_response(self, request, response):return responseclass CsrfViewMiddleware(MiddlewareMixin):def process_request(self, request):csrf_token = self._get_token(request)if csrf_token is not None:# Use same token next time.request.META['CSRF_COOKIE'] = csrf_tokendef process_view(self, request, callback, callback_args, callback_kwargs):return self._accept(request)def process_response(self, request, response):return responseclass AuthenticationMiddleware(MiddlewareMixin):def process_request(self, request):request.user = SimpleLazyObject(lambda: get_user(request))
django支持程序员自定义中间件并且暴露给程序员五个可以自定义的方法
# 1.必须掌握process_request(self,request)process_response(self,request,response)# 2.了解即可process_view(self,view_name,*args,**kwargs)process_template_response(self,request,response)process_exception(self,request,exception)
自定义中间件
# 1.在项目名或者应用名下创建一个任意名称的文件夹# 2.在该文件夹内创建一个任意名称的py文件# 3.在该py文件内需要书写类(这个类必须继承MiddlewareMixin)from django.utils.deprecation import MiddlewareMixin然后在这个类里面就可以自定义五个方法了(这五个方法并不是全部都需要书写,用几个写几个)# 4.需要将类的路径以字符串的形式注册到配置文件中才能生效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','你自己写的中间件的路径1','你自己写的中间件的路径2','你自己写的中间件的路径3',]
案例
1、在应用名下创建一个名为mymiddleware的文件夹
2、在该文件夹内创建一个mydd.py文件

3.在该py文件内需要书写类(这个类必须继承MiddlewareMixin)
mydd.py
from django.utils.deprecation import MiddlewareMixinclass MyMiddleware1(MiddlewareMixin):def process_request(self,request):print("我是第一个自定义中间件的process_request方法")def process_response(self,request,response):""":param request::param response: 就是django后端返回给浏览器的内容:return:必须返回response"""print("我是第一个自定义中间件的process_response方法")return responseclass MyMiddleware2(MiddlewareMixin):def process_request(self,request):print("我是第二个自定义中间件的request方法")def process_response(self,request,response):""":param request::param response: 就是django后端返回给浏览器的内容:return:response"""print("我是第二个自定义中间件的process_response方法")return response
4、需要将类的路径以字符串的形式注册到配置文件中才能生效
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',# 注册自己的中间件'app01.mymiddleware.mydd.MyMiddleware1','app01.mymiddleware.mydd.MyMiddleware2',]
urls.py
from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [url(r'^admin/', admin.site.urls),url(r'^index/', views.index),]
views.py
from django.shortcuts import render,HttpResponse# Create your views here.def index(request):print("我是视图函数index")return HttpResponse("index")
浏览器访问

查看后端输出

总结
"""1.必须掌握process_request1.请求来的时候需要经过每一个中间件里面的process_request方法结果的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行2.如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件3.如果该方法返回了HttpResponse对象,那么请求将不再继续往后执行而是直接原路返回(就相当于校验失败不允许访问...)process_request方法就是用来做全局相关的所有限制功能process_response1.响应走的时候需要结果每一个中间件里面的process_response方法该方法有两个额外的参数request,response2.该方法必须返回一个HttpResponse对象1.默认返回的就是形参responsereturn response2.你也可以自己返回自己的(狸猫换太子)return HttpResponse("Hello world!")3.顺序是按照配置文件中注册了的中间件从下往上依次经过如果你没有定义的话 直接跳过执行下一个研究如果在第一个自定义中间件process_request方法就已经返回了HttpResponse对象,那么响应走的时候是经过所有的中间件里面的process_response还是有其他情况是其他情况结论:如果第一个自定义中间件process_request方法就已经返回了HttpResponse对象,那么响应走的时候会直接走同级别的的第一个自定义中间件中的process_reponse返回flask框架也有一个中间件但是它的规律只要返回数据了就必须经过所有中间件里面的类似于process_reponse方法2.了解即可process_view路由匹配成功之后执行视图函数之前,会自动执行中间件里面的该放法顺序是按照配置文件中注册的中间件从上往下的顺序依次执行process_template_response返回的HttpResponse对象有render属性的时候才会触发顺序是按照配置文件中注册了的中间件从下往上依次经过# mydd.pydef process_template_response(self,request,response):print('我是第一个自定义中间件里面的process_template_response')return response# views.pydef index(request):print('我是视图函数index')obj = HttpResponse('index')def render():print('内部的render')return HttpResponse("O98K")obj.render = renderreturn objprocess_exception当视图函数中出现异常的情况下触发顺序是按照配置文件中注册了的中间件从下往上依次经过"""
csrf跨站请求伪造
"""钓鱼网站我搭建一个跟正规网站一模一样的界面(中国银行)用户不小心进入到了我们的网站,用户给某个人打钱打钱的操作确确实实是提交给了中国银行的系统,用户的钱也确确实实减少了但是唯一不同的时候打钱的账户不适用户想要打的账户变成了一个莫名其妙的账户大学英语四六级考之前需要学生自己网站登陆缴费内部本质我们在钓鱼网站的页面 针对对方账户 只给用户提供一个没有name属性的普通input框然后我们在内部隐藏一个已经写好name和value的input框如何规避上述问题csrf跨站请求伪造校验网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识当这个页面朝后端发送post请求的时候 我的后端会先校验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden),如果成功则正常执行"""
钓鱼网站案例:
正规网站:
urls.py
from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [url(r'^admin/', admin.site.urls),# 转账url(r'^transfer/', views.transfer),]
views.py
from django.shortcuts import render,HttpResponse# Create your views here.def transfer(request):if request.method == "POST":username = request.POST.get("username")target_user = request.POST.get("target_user")money = request.POST.get("money")print("%s 给 %s 转了%s元" %(username,target_user,money))return render(request,"transfer.html")
transfer.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><meta name="viewport" content="width=device-width, initial-scale=1"><link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script><script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script></head><body><h1>我是正儿八经的网站</h1><form action="" method="post"><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></body></html>
钓鱼网站:
urls.py
from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [url(r'^admin/', admin.site.urls),url(r'^transfer/', views.transfer),]
views.py
from django.shortcuts import render# Create your views here.def transfer(request):return render(request,"transfer.html")
transfer.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><meta name="viewport" content="width=device-width, initial-scale=1"><link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script><script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script></head><body><h1>我是钓鱼网站</h1><form action="http://127.0.0.1:8000/transfer/" method="post"><p>username:<input type="text" name="username"></p><p>target_user:<input type="text"></p><input type="hidden" name="target_user" value="jason"><p>money:<input type="text" name="money"></p><input type="submit"></form></body></html>
如何符合csrf校验
form表单如何符合校验,在form表单中任意位置添加{% csrf_token %}
<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>

此时,上面钓鱼网站例子中钓鱼网站提交到正规的网站时,就会403
ajax如何符合校验
方式一:利用标签查找获取页面上的随机字符串,data中添加一个键csrfmiddlewaretoken(名字必须叫这个),值为拿到的随机字符串
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><meta name="viewport" content="width=device-width, initial-scale=1"><link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script><script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script></head><body><h1>我是正儿八经的网站</h1><form action="/csrf/" method="post"><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><button id="d1">ajax请求</button><script>$("#d1").click(function () {$.ajax({url:"",type:"post",// 方式一:利用标签查找获取页面上的随机字符串data:{"username":'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},success:function () {}})})</script></body></html>
方式二:利用模版语法提供的快捷书写
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><meta name="viewport" content="width=device-width, initial-scale=1"><link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script><script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script></head><body><h1>我是正儿八经的网站</h1><form action="/csrf/" method="post"><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><button id="d1">ajax请求</button><script>$("#d1").click(function () {$.ajax({url:"",type:"post",// 第二种 利用模版语法提供的快捷书写data:{"username":'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},success:function () {}})})</script></body></html>
方式三:通用方式直接拷贝js代码并应用到自己的html页面上即可
配置静态文件

mysteup.js
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 protectionreturn (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));}$.ajaxSetup({beforeSend: function (xhr, settings) {if (!csrfSafeMethod(settings.type) && !this.crossDomain) {xhr.setRequestHeader("X-CSRFToken", csrftoken);}}});mysteup.js
要引用的html文件
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><meta name="viewport" content="width=device-width, initial-scale=1"><link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script><script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script></head><body><h1>我是正儿八经的网站</h1><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><button id="d1">ajax请求</button><!--动态引入静态文件-->{% load static %}<script src="{% static "js/mysteup.js" %}"></script><script>$("#d1").click(function () {$.ajax({url:"",type:"post",data:{"username":'jason'},success:function () {}})})</script></body></html>
csrf相关装饰器
"""1.网站整体都不校验csrf,就单单几个视图函数需要校验2.网站整体都校验csrf,就单单几个视图函数不校验"""
两个装饰器:
from django.views.decorators.csrf import csrf_protect,csrf_exempt"""csrf_protect 需要校验针对csrf_protect符合我们之前所学的装饰器的三种玩法csrf_exempt 忽视校验针对csrf_exempt只能给dispatch方法加才有效"""
FBV上使用:
from django.views.decorators.csrf import csrf_protect,csrf_exempt"""csrf_protect 需要校验针对csrf_protect符合我们之前所学的装饰器的三种玩法csrf_exempt 忽视校验针对csrf_exempt只能给dispatch方法加才有效"""# @csrf_exempt# @csrf_protectdef transfer(request):if request.method == 'POST':username = request.POST.get('username')target_user = request.POST.get('target_user')money = request.POST.get('money')print('%s给%s转了%s元'%(username,target_user,money))return render(request,'transfer.html')
CBV上使用
from django.views.decorators.csrf import csrf_protect,csrf_exemptfrom django.utils.decorators import method_decoratorfrom django.views import View"""csrf_protect 需要校验针对csrf_protect符合我们之前所学的装饰器的三种玩法csrf_exempt 忽视校验针对csrf_exempt只能给dispatch方法加才有效"""# @method_decorator(csrf_protect,name='post') # 针对csrf_protect 第二种方式可以# @method_decorator(csrf_exempt,name='post') # 针对csrf_exempt 第二种方式不可以@method_decorator(csrf_exempt,name='dispatch')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_exempt还可以通过在urls.py给CBV类装饰
from django.conf.urls import urlfrom django.views.decorators.csrf import csrf_exemptimport viewsurlpatterns = [url(r'^myview/$', csrf_exempt(views.MyView.as_view()), name='myview'),]
补充知识点
# 模块:importlibimport importlibres = 'myfile.b'ret = importlib.import_module(res) # from myfile import b# 该方法最小只能到py文件名print(ret)
根据中间件的settings推导出重要思想
在settings.py文件中配置功能模块文件夹字符串,想用的时候打开,不想用直接注释掉(全局就不可以用这个功能)
案例发送通知(通过QQ、微信。。。)
正常思路:比较麻烦,如果像去掉一个功能很费劲
notify.py
def wechat(content):print("微信通知:%s" %content)def qq(content):print("QQ通知:%s" %content)def email(content):print("邮箱通知:%s" %content)
start.py
from notify import *def send_all(content):wechat(content)qq(content)email(content)if __name__ == '__main__':send_all("这是个重要思想")
使用中间件中的通过字符串导入模块方法的思想
目录结构:
─test│├─notify│ ├─__init__.py│ ││ ├─email.py│ ││ ├─qq.py│ ││ └─wechat.py│├─settings.py│└─start.py
notify文件夹
email.py
class Email(object):def __init__(self):passdef send(self,content):print("邮箱通知:%s" %content)
qq.py
class QQ(object):def __init__(self):passdef send(self,content):print("QQ通知:%s" %content)
wechat.py
class Wechat(object):def __init__(self):passdef send(self,content):print("微信通知:%s" %content)
init.py
import settingsimport importlibdef send_all(content):for path_str in settings.NOTIFY_LIST: # path_str 'notify.email.Email'module_path,class_name = path_str.rsplit(".",1)# module_path = 'notify.email'# 利用字符串导入模块module = importlib.import_module(module_path) # from notify import email# 利用反射获取类名cls = getattr(module,class_name) # 拿到真正类方法的内存地址# 生成类的对象obj = cls()# 利用鸭子类型直接调用send方法obj.send(content)__init__.py
settings.py
NOTIFY_LIST = ['notify.email.Email','notify.wechat.Wechat','notify.qq.QQ',]
start.py
import notifynotify.send_all('这是个非非常重要的思想')
