首页 > 其他分享 >10 Django中间件

10 Django中间件

时间:2023-02-13 07:44:36浏览次数:40  
标签:10 process request 中间件 Django csrf django response

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

相关文章

  • 06 Django与Ajax
    Django与Ajax什么是JSONJSON是轻量级的文本数据交换格式,JSON使用JavaScript语法来描述数据对象,但是JSON仍然独立于语言和平台。JSON解析器和JSON库支持许多不......
  • 01 Django简介
    前戏Wsgiref模块封装了socket代码请求来的时候将http数据格式拆封成一个大字典响应走的时候将数据打包成符合http协议要求的数据格式#模块封装了socket代码并将请求......
  • 代码随想录算法Day11 | 20. 有效的括号,1047. 删除字符串中的所有相邻重复项,逆波兰表达
     20.有效的括号题目链接: 20.有效的括号-力扣(LeetCode)题目给定一个只包括'(',')','{','}','[',']' 的字符串s,判断字符串是否有效。有效字符串需满足:左括号必须用......
  • 10、常见的transforms中的类
    *****其实就是学会使用trannsforms的.py文件中的不同class类主要关注点:*输入  *输出   *作用图片的输入数据类型:         打开方式:PIL......
  • 2021 OWASP top 10
    Top1:失效的访问控制      失效的访问控制,也叫越权,指的是在未对通过身份验证的用户,实施恰当的访问控制。攻击者可以利用这一漏洞,访问未经授权的功能或数据。比如,访......
  • 【DFS】LeetCode 108. 将有序数组转换为二叉搜索树
    题目链接108.将有序数组转换为二叉搜索树思路类似于二分搜索,定位到数组中间mid,然后左边的子数组构成左子树,右边的子数组构成右子树,mid处的数字构成根结点。递归构建......
  • 基于Django开发的小型超市收银系统及各种炒股指标信息
    登录页面注册修改密码诗词推荐背景刷新更换登录 首页今日收益金额日收益折线图商品热度排行商品预警信息今日待办任务诗词推荐      ......
  • x210-2023-02-11
    1、secureCRT中文不乱码,而英文有部分出现乱码,譬如下面图中乱码位置正常应该显示的是asm,但是其余英文并没有乱码,会话选项的字体原来设置的是新宋体,字符编码为默认,字符集为GB......
  • 戴尔T5810电脑 Hackintosh 黑苹果efi引导文件
    原文来源于黑果魏叔官网,转载需注明出处。硬件型号驱动情况主板戴尔T5810,C610/612芯片处理器英特尔至强E5-2620v3已驱动内存12GB已驱动硬盘500GBWDBlueSolidStateDriv......
  • 0元白嫖ChatGPT只要10秒钟-亲测无限制
    需求背景前面发了一篇《全网最详细的ChatGPT注册-你和ChatGPT的距离只差这篇文章》,短短几个小时,阅读量就上1000了。搭着chatGPT的热度,我也蹭了一些流量。在前面的文章中,注册......