Django中间件
中间件介绍
django中间件类似于是django的保安,请求来的时候需要先经过中间件才能到达django后端,响应走的时候也需要经过中间件才能到达web服务网关接口
django中间件可以用来做什么:网站全局的身份校验、访问频率限制、权限校验等,只要是涉及到全局的校验都可以在中间件中完成
中间件是在视图函数执行之前和执行之后做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定时间去执行这些方法。
settings.py中django默认的七个中间件:MIDDLEWARE配置项是一个列表,列表中是一个个字符串,这些字符串其实是一个个类,也就是一个个中间件。
# from django.middleware.security import SecurityMiddleware
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',
]
自定义中间件
中间件可以定义五个方法,分别是:(主要的是process_request和process_response)
- process_request(self,request)
- process_response(self, request, response)
- process_view(self, request, view_func, view_args, view_kwargs)
- process_exception(self, request, exception)
- process_template_response(self,request,response)
以上方法的返回值可以是None或HttpResponse对象,如果是None则继续按照django定义的规则向后执行,如果是HttpResponse对象则直接将该对象返回给用户
在书写中间件时,只要形参中有repsonse就顺手将其返回,这个reponse就是要返回给前端的数据
自定义一个中间件示例
process_request
process_request方法是在视图函数之前执行的,请求来的时候会依次经过每个中间件里面的process_request方法(从上往下)
如果方法里返回了HttpResponse对象那么会直接返回不再往下执行,基于该特点就可以做访问频率限制、身份校验、权限校验等
request参数和视图函数中的request是一样的,在交给Django路由之前可以对这个request对象进行一系列的操作,后续视图函数中即可获取到在中间件中设置的值
process_response
必须将response形参返回,因为这个形参指代的就是要返回给前端的数据
process_response方法是在视图函数之后执行的,响应走的时候会依次经过每个中间件里面的process_response方法(从下往上)
request参数跟process_request方法一样,response参数是视图函数返回的HttpResponse对象
process_view
process_view(self, request, view_func, view_args, view_kwargs)
view_func是将使用的视图函数对象
view_args是将传递给视图函数的位置参数的列表
view_kwargs是将传递给视图函数的关键字参数的字典,view_args和view_kwargs都不包含第一个视图参数request
在路由匹配成功之后执行视图函数之前触发,执行顺序按照中间件注册顺序的正序执行
应该返回None或HttpResponse对象。如果返回None,Django将继续处理这个请求,执行其他中间件的process_view方法,然后再执行相应的视图函数。如果返回HttpResponse对象,那么将不会执行视图函数,而是直接在中间件中掉头,倒叙执行一个个process_response方法,最后返回给浏览器
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request(self, request):
print("MD1里面的 process_request")
def process_response(self, request, response):
print("MD1里面的 process_response")
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-" * 80)
print("MD1 中的process_view")
print(view_func, view_func.__name__)
class MD2(MiddlewareMixin):
def process_request(self, request):
print("MD2里面的 process_request")
pass
def process_response(self, request, response):
print("MD2里面的 process_response")
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-" * 80)
print("MD2 中的process_view")
print(view_func, view_func.__name__)
"""
访问index视图函数,输出结果如下:
MD1里面的 process_request
MD2里面的 process_request
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x000001DE68317488> index
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x000001DE68317488> index
app01 中的 index视图
MD2里面的 process_response
MD1里面的 process_response
"""
process_exception
process_exception(self, request, exception)
exception是视图函数异常产生的Exception对象
只有在视图函数报错时才执行,返回值可以是None也可以是HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件process_response方法,并返回给浏览器,否则将默认处理异常。如果返回None,则交给下一个中间件的process_exception方法来处理异常。执行顺序按照中间件注册顺序的倒序执行
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_exception(self, request, exception):
print(exception)
print("MD2 中的process_exception")
class MD2(MiddlewareMixin):
def process_exception(self, request, exception):
print(exception)
print("MD2 中的process_exception")
return HttpResponse(str(exception)) # 返回一个响应对象
"""
# views.py
def index(request):
print("app01 中的 index视图")
raise ValueError("呵呵") # 如果视图函数中无异常,process_exception方法不执行
return HttpResponse("O98K")
访问index视图函数,输出结果如下:
app01 中的 index视图
呵呵
MD2 中的process_exception
注意,没有执行MD1的process_exception方法,因为MD2中的process_exception方法直接返回了一个响应对象
"""
process_template_response(用的比较少)
process_template_response(self, request, response)
process_template_response是在视图函数执行完成后立即执行,执行顺序按照中间件注册顺序的倒序执行。但有一个前提条件,那就是视图函数返回的HttpResponse对象中必须有render属性才会触发
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_template_response(self, request, response):
print("MD1 中的process_template_response")
return response
class MD2(MiddlewareMixin):
def process_template_response(self, request, response):
print("MD2 中的process_template_response")
return response
"""
# views.py
def index(request):
print("app01 中的 index视图")
def render():
print("in index/render")
return HttpResponse("O98K")
rep = HttpResponse("OK")
rep.render = render
return rep
访问index视图函数,输出结果如下:
app01 中的 index视图
MD2 中的process_template_response
MD1 中的process_template_response
in index/render
从结果看出:执行了视图函数返回的HttpResponse对象的render方法,返回了一个新的HttpResponse对象
"""
csrf中间件解决跨站请求伪造
介绍
钓鱼网站:
通过制作一个跟正儿八经网站一模一样的页面,骗取用户输入信息从而做手脚,转账交易的请求确实是发给了银行,账户的钱也是确实是减少了,但唯一不一样的地方在于收款人账户不对
内部原理:在让用户输入对方账户的input上做手脚,给这个input不设置name属性,在内部隐藏一个事先写好的name和value属性的input,这个value的值就是钓鱼网站受益者账号
csrf中间件防止钓鱼网站:
会给返回给用户的form表单页面塞一个随机字符串,请求来时会先比对随机字符串是否一致,如果不一致直接拒绝(403)。该随机字符串不同浏览器或者同一个浏览器每次访问都不一样
csrf中间件打开后,不论form表单还是是ajax,只要是向django提交post请求,都必须校验csrf_token来防伪跨站请求
form表单设置csrf_token
<form action="/lll/" method="post">
{% csrf_token %} <!-- 自动生成一个name属性值为csrfmiddlewaretoken,value值为随机字符串的一个input标签,并且是隐藏的 -->
...
</form>
AJAX请求设置csrf_token
方式一:通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送
{% csrf_token %}
<script>
$.ajax({
url: "/cookie_ajax/",
type: "POST",
data: {
"username": "Tonny",
"password": 123456,
"csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val() // 使用JQuery取出csrfmiddlewaretoken的值,拼接到data中
},
success: function (data) {
console.log(data);
}
})
</script>
方式二:通过获取返回的cookie中的字符串放置在请求头中发送。注意:需要引入一个jquery.cookie.js插件
{% csrf_token %}
<script>
$.ajax({
url: "/cookie_ajax/",
type: "POST",
headers: {"X-CSRFToken": $.cookie('csrftoken')}, // 从Cookie取csrf_token,并设置ajax请求头
data: {"username": "Q1mi", "password": 123456},
success: function (data) {
console.log(data);
}
})
</script>
方式三:直接写{{ csrf_token }}
{% csrf_token %}
<script>
$.ajax({
url: "/cookie_ajax/",
type: "POST",
data: {
"username": "Tonny",
"password": 123456,
"csrfmiddlewaretoken": '{{ csrf_token }}'
},
success: function (data) {
console.log(data);
}
})
</script>
方式四:将获取随机键值对的方法写到一个js文件中,之后只需要导入该文件即可(依赖jquery)。在static文件夹下新建一个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_token %}
{% load static %}
<script src="{% static 'setjs.js' %}"></script>
<script>
$.ajax({
url: "/cookie_ajax/",
type: "POST",
data: {
"username": "Tonny",
"password": 123456
},
success: function (data) {
console.log(data);
}
})
</script>
全局检验csrf个别不校验
csrf_protect:为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件
csrf_exempt:取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件
FBV
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
def login(request):
return HttpResponse('login')
CBV
装饰器中只有csrf_exempt是特例,其他的装饰器在给CBV装饰时都有三种方式
如果是csrf_exempt 只有两种(只能给dispatch装) 特例
from django.views import View
from django.views.decorators.csrf import csrf_exempt,csrf_protect
from django.utils.decorators import method_decorator
@method_decorator(csrf_exempt,name='dispatch') # 方式二
class MyView(View):
# @method_decorator(csrf_exempt) # 方式一
def dispatch(self, request, *args, **kwargs):
res = super().dispatch(request, *args, **kwargs)
return res
def get(self,request):
return HttpResponse('get')
def post(self,request):
return HttpResponse('post')
全局不校验csrf个别校验
FBV
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_protect
def lll(request):
return HttpResponse('lll')
CBV
from django.views import View
from django.views.decorators.csrf import csrf_exempt,csrf_protect
from django.utils.decorators import method_decorator
# 这两个装饰器在给CBV装饰的时候 有一定的区别
如果是csrf_protect 那么有三种方式
# @method_decorator(csrf_protect,name='post') # 方式一
class MyView(View):
@method_decorator(csrf_protect) # 方式三
def dispatch(self, request, *args, **kwargs):
res = super().dispatch(request, *args, **kwargs)
return res
def get(self,request):
return HttpResponse('get')
# @method_decorator(csrf_protect) # 方式二
def post(self,request):
return HttpResponse('post')
csrf中间件版登录验证
中间件版的登录验证需要依靠session,所以数据库中要有django_session表
urls.py
from app02 import views as v2
urlpatterns = [
path('admin/', admin.site.urls),
path('login/',v2.login),
path('home/',v2.home),
path('index/',v2.index)
]
views.py
from django.shortcuts import render,redirect,HttpResponse
from app02 import models
# Create your views here.
def login(request):
error_msg=''
if request.method=='POST':
username=request.POST.get('username')
password=request.POST.get('password')
user_obj=models.User.objects.filter(username=username,password=password)
if user_obj:
#设置session
request.session['login']='ok'
#获取用户想直接访问的URL
url=request.GET.get('next')
#如果有,就跳转到客户初始想访问的URL
if not url:
#没有则默认跳转到home页面
url='/home/'
return redirect(url)
else:
error_msg='username or password error!'
return render(request,'login.html',{'error_msg':error_msg})
def home(request):
return HttpResponse('<h1>这是home页面 只有登录了才能看到</h1>')
def index(request):
return HttpResponse('<h1>这是index页面 也只有登录了才能看到<h1>')
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆页面</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<label for="">username:<input type="text" name="username"></label>
<label for="">password:<input type="password" name="password"></label>
<input type="submit" value="submit">
</form>
<h1 style="color: red">{{ error_msg }}</h1>
</body>
</html>
middlewares.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect
class Check_Login(MiddlewareMixin):
def process_request(self,request):
next_url=request.path_info
if not next_url.startswith('/login/'):
is_login=request.session.get('login','')
if not is_login:
return redirect('/login/?next={}'.format(next_url))
在settings.py中注册
MIDDLEWARE = [
...
'django.middleware.csrf.CsrfViewMiddleware',
'middleware.my_middleware.Check_Login',
]
标签:10,process,request,中间件,Django,csrf,django,response
From: https://www.cnblogs.com/jlllog/p/17115186.html