Django——中间件
中间件
django请求生命周期流程图
django中间件
'''
django中间件就好比django后端跟浏览器交互的门卫
1、请求来的时候需要经过中间件才能真正到达django后端
2、响应走的时候也需要经过中间件才能发出去
django自带有7个门卫,即django自带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',
]
'''
from django.contrib.sessions.middleware import SessionMiddleware
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 response
from django.middleware.csrf import CsrfViewMiddleware
class 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_token
def process_view(self, request, callback, callback_args, callback_kwargs):
return self._accept(request)
def process_response(self, request, response):
return response
from django.contrib.auth.middleware import AuthenticationMiddleware
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
request.user = SimpleLazyObject(lambda: get_user(request))
如何自定义中间件
'''
1、在项目名或者应用名下创建一个任意名称的文件夹
2、在该文件夹内创建一个任意名称的py文件
3、在该py文件内需要数写类,该类必须继承MiddlewareMixin
在这个类里面就可以自定义五个方法了
4、去sesstins配置文件的中间件部分(MIDDLEWARE)注册中间件
'''
# 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',
# 注册自己的中间件(在应用下创建路径有提示,但是如果在项目下创建就没有提示了 你就需要自己比对着书写)
# 'app01.mymiddleware.mydd.MyMiddleware1',
# 'app01.mymiddleware.mydd.MyMiddleware2'
]
# app01/mymiddleware/mydd.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MyMiddleware1(MiddlewareMixin):
def process_request(self,request):
print('我是第一个自定义中间件里面的process_request方法')
# return HttpResponse('baby!')
def process_response(self,request,response):
"""
:param request:
:param response: 就是django后端返回给浏览器的内容
:return:
"""
print('我是第一个自定义中间件里面的process_response方法')
return response
def process_view(self,request,view_name,*args,**kwargs):
print(view_name,args,kwargs)
print('我是第一个自定义中间件里面的process_view')
def process_template_response(self,request,response):
print('我是第一个自定义中间件里面的process_template_response')
return response
def process_exception(self,request,exception):
print('我是第一个中间件里面的process_exception')
print(exception)
class MyMiddleware2(MiddlewareMixin):
def process_request(self,request):
print('我是第二个自定义中间件里面的process_request方法')
def process_response(self,request,response):
print('我是第二个自定义中间件里面的process_response方法')
return response
def process_view(self,request,view_name,*args,**kwargs):
print(view_name,args,kwargs)
print('我是第二个自定义中间件里面的process_view')
def process_template_response(self,request,response):
print('我是第二个自定义中间件里面的process_template_response')
return response
def process_exception(self,request,exception):
print('我是第二个中间件里面的process_exception')
print(exception)
中间件的五大方法
1、process_request
'''
1、请求来时,经过一个个的中间件中的process_request方法,
执行顺序按照配置文件中注册的中间件从上往下的顺序一依次执行
2、如果定义的中间件中没有该方法,那么就会跳过该中间件直接执行下一个中间件
3、如果该方法返回了HttpResponse对象,那么就不会再继续往下执行,直接来到同级的process_response方法,开始原路返回(eg:校验失败不允许访问)
process_request就是用来做全局相关的所有限制功能
'''
2、process.response
'''
1、响应走的时候需要经过每一个中间件的process.response方法,
有两个额外参数:request、response
2、必须要有一个HttpResponse对象的返回值
1):默认返回形参response
2):可以返回自己的一个HttpResponse对象
3、执行顺序:按照配置文件中注册的中间件从下往上依次执行
如果中间件内没有定义该方法则直接跳过该中间件执行下一个
'''
# 当第一个process_request方法返回HttpResponse对象,那么响应走的时候会直接从同级别的process_response开始往上返回
'''
flask框架也有一个中间件但是它的规律
只要返回数据了就必须经过所有中间件里面的类似于process_reponse方法
'''
3、process_view
'''
触发条件:路由匹配成功后执行视图函数之前,自动触发执行中间件中的process_view方法
执行顺序同样也是按照配置文件中注册的中间件由上往下依次执行
'''
4、process_excplition
'''
触发条件:当视图函数中出现异常时候会触发中间件内的process_excplition方法
执行顺序同样也是按照配置文件中注册的中间件由下往上依次执行
'''
5、process_template_response
'''
触发条件:当返回的HttpResponse对象中有render属性时才会触发
即必须满足如下视图函数中的返回格式,才会触发
# 视图函数index
def index(request):
print('我是视图函数index')
obj = HttpResponse('index')
def render():
print('内部的render')
return HttpResponse("O98K")
obj.render = render
return obj
执行顺序同样也是按照配置文件中注册的中间件由下往上依次执行
'''
图解
- process_request方法返回HttpResponse对象,直接执行同级别的process_response方法将对象返回
- process_view方法返回HttpResponse对象,直接从下往上执行中间件的process_response方法将对象返回
django请求生命周期流程图完整版
csrf跨站请求伪造
'''
钓鱼网站(以银行钓鱼网站为例): 与正规网站的页面布局一摸一样,唯一不同的就是在表单提交的信息
内部本质:
我们在钓鱼网站的页面 针对对方账户 只给用户提供一个没有name属性的普通input框
然后我们在内部隐藏一个已经写好name和value的input框
通过csrf跨站请求伪造校验规避上述问题
csrf跨站请求伪造校验
网站再给用户返回一个具有提交数据功能页面的时候会给该页面加一个唯一标识
当该页面朝后端发送post请求时会优先校验唯一标识,如果唯一标识不正确,就直接拒绝访问(403 forbbiden),如果成功就正常执行
'''
符合csrf跨站请求伪造校验
# form表单
<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>
# ajax请求三种符合校验的方法
'''
方法一:通过标签查找,获取页面上的随机字符串
data:{"username":'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()}
方法二:利用模版语法提供的快捷书写(针对非前后端分离项目)
data:{"username":'jason','csrfmiddlewaretoken':'{{ csrf_token }}'}
方法三:使用django官方提供的js代码来解决
{% load static %}
<script src="{% static 'js/mysetup.js' %}"></script>
data:{"username":'jason'}
'''
mysetup.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 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相关装饰器(针对post请求)
'''
需求1.网站整体都不校验csrf,就单单几个视图函数需要校验
需求2.网站整体都校验csrf,就单单几个视图函数不校验
from django.views.decorators.csrf import csrf_protect,csrf_exempt
from django.utils.decorators import method_decorator
csrf_protect 需要校验
针对csrf_protect给CBV加装饰器的三种方法加均有效(403 forbidden)
csrf_exempt 忽视校验
针对csrf_exempt只有给dispatch方法加才有效(不会报403 forbidden)
'''
# @csrf_exempt
# @csrf_protect
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')
方法一:对类中方法加装饰器
# 针对装饰器csrf_protect,有效
# 针对装饰器csrf_exempt 无效
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect,csrf_exempt
from django.shortcuts import render, HttpResponse
from django.views import View
class Login(View):
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self, request):
print('get')
return render(request, 'login.html')
@method_decorator(csrf_protect) # 有效(403 forbidden)
@method_decorator(csrf_exempt) # 无效(403 forbidden)
def post(self, request):
print('post')
return HttpResponse('POST')
方法二:对类加装饰器
# 针对装饰器csrf_protect 有效
# 针对装饰器csrf_exempt指定加给除dispatch以外的方法 无效
# 针对装饰器csrf_exempt指定加给dispatch方法 有效
@method_decorator(csrf_protect, name='post') # 有效(403 forbidden)
@method_decorator(csrf_exempt, name='post') # 无效(403 forbidden)
@method_decorator(csrf_exempt, name='dispatch') # 有效(post)
class Login(View):
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self, request):
print('get')
return render(request, 'login.html')
def post(self, request):
print('post')
return HttpResponse('POST')
方法三:对类的dispatch方法加装饰器
# 针对装饰器csrf_protect 有效
# 针对装饰器csrf_exempt 有效
class Login(View):
@method_decorator(csrf_protect) # 有效(403 forbidden)
@method_decorator(csrf_exempt) # 有效(post)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self, request):
print('get')
return render(request, 'login.html')
def post(self, request):
print('post')
return HttpResponse('POST')
补充知识点(importlib模块)
import importlib
res = 'myfile.b'
ret = importlib.import_module(res) # 就相当于 from myfile import b
# 该方法最小只能到py文件名,无法到py文件下的具体名字
print(ret)
重要编程思想
# 将功能代码通过配置文件的方式实现是否展示
'''
settings.py
notify
__init__.py|
email.py |
qq.py |
wechat.py |
start.py
'''
-
settings
# 以后只需要在notify文件夹内添加py文件然后在配置文件内注册即可展示对应功能 NOTIFY_LIST = [ 'notify.email.Email', 'notify.qq.QQ', 'notify.wechat.WeChat' ]
-
notify
# email.py class Email: def __inti__(self): pass def send(self, content): print('邮箱通知:%s' % content) # qq.py class QQ: def __inti__(self): pass def send(self, content): print('QQ通知:%s' % content) # wechat.py class WeChat: def __inti__(self): pass def send(self, content): print('微信通知:%s' % content) # __init__.py import settings import importlib def send_all(content): for module_path in settings.NOTIFY_LIST: module_name,cls_name = module_path.rsplit('.', maxsplit=1) module = importlib.import_module(model_name) # 等价于 from notify import model_name cls_obj = getattr(module, cls_name)() cls_obj.send(content)
-
start.py
import notify notify.send_all('重要思想!重要思想!重要思想!重要的事情说三遍') ''' 邮箱通知:重要思想!重要思想!重要思想!重要的事情说三遍 qq通知:重要思想!重要思想!重要思想!重要的事情说三遍 微信通知:重要思想!重要思想!重要思想!重要的事情说三遍 '''