• day70 django中间件


    一、django的七个中间件

    django中间件是浏览器和服务端交互的第一个门栏,请求来的时候需要通过中间件,响应走的时候也需要进过中间件

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware'
        from django.middleware.security import 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',
    ]
    
    # 以上代码相当于中间件的导入,通过一些内部的操作转换成下面的代码
    'django.middleware.security.SecurityMiddleware'
    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 response
          
    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
          
    class AuthenticationMiddleware(MiddlewareMixin):
        def process_request(self, request):
            request.user = SimpleLazyObject(lambda: get_user(request))
            
    '''
    django支持程序员自定义中间件,并且有五个可以自定义的方法
    	1 必须掌握
    		process_request
    		prpcess_respone
    		
    	2 了解即可
    		process_view
    		processs_template_respone
    		process_exception
    '''
    

    二、如何自定义中间件

    1 自定义中间件的创建方式

    1. 先再项目下或者应用下创建一个任意名称的文件夹
    2. 在项目文件下内创建一个任意名称的py文件
    3. 在该py文件内书写类,这个类必须继承MiddlewareMixin,然后在这个类中自定制我们的需要的django提供的五个方法,用什么写什么,没有必须要写的
    4. 将类的路径以字符串的形式注册到配置文件中生效

    2 django提供自定制中间件的五个方法

    2.1 必须掌握两个方法

    process_request

    特点:

    • 所有请求来到服务端的时候先要依次(按照书写顺序的先后)经过所有中间件的process_request方法(没有就跳过)
    • 如果这个函数返回了一个HttpRespone对象,那么这个请求就会从这个函数开始原路返回(会先执行同中间件的process_respone,再依次往上返回)
    • 这些特点可以让这个方法作为全局相关的所有限制功能
    class Mymid1(MiddlewareMixin):
        def process_request(self,request):
            print('我是第一个中间件的process_request')
    

    process_response

    特点:

    • 所有响应返回的时候都要依次(与书写顺序相反)经过所有中间件的process_response方法(没有就跳过)
    • 必须返回respons对象
      • 返回的相当于是应该响应的数据
    • 或者返回一个HttpResponse对象
      • 返回的相当于是把应该相应的数据替换成我们自己写的数据(偷梁换柱)

    ps:flask也有中间件,它的规律是只要返回了数据就必须要经过所有中间件里类似process_response方法

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import render,HttpResponse
    
    class Mymid1(MiddlewareMixin):
        def process_request(self,request):
            print('我是第一个中间件的process_request')
    
        def process_response(self,request,response):
            print('我是第一个中间件的process_respone')
            return HttpResponse('123') # 截胡
    
    class Mymid2(MiddlewareMixin):
        def process_request(self,request):
            print('我是第二个中间件的process_request')
    
        def process_response(self, request, response):
            print('我是第二个中间件的process_respone')
            return response # 正常返回
    

    2.2 了解方法

    process_view

    • 在路由匹配成功之后,执行视图函数之前会执行此方法
    • 所以只会在请求的时候执行
    • 执行顺序是中间件从上往下
    def process_view(self,request,view_name,args,kwargs):
        # print(view_name,args,kwargs)
        print('我是第一个中间件的process_view')
    

    process_template_response

    • 当视图函数返回的HttpRespone对象中有render属性的时候会触发
    • 执行顺序是中间件从下往上
    # 中间件中
    def process_template_response(self, request, response):
        print('我是第二个中间件的process_template_response')
        return response
    
    # 视图函数中
    def index(request):
        print('我是视图函数index')
        obj = HttpResponse('index')
    
        def render():
            print('内部的render')
            return HttpResponse("O98K")
        obj.render = render
        return obj
    

    process_exception

    • 当视图函数出现异常的时候触发(比如写了一个名字但是没定义)
    • 顺序按照中间件从下往上
    def process_exception(self, request, exception):
        print('我是第二个中间件的process_exception')
        print(exception) # 报错信息
    

    三、csrf跨站请求伪造校验

    1 案例及解决方案

    案例:钓鱼网站

    ​ 用户在一个缴费钓鱼网站给自己制定的用户转钱,结果转到了另一个人账号上

    原因:

    ​ 用户在打开缴费钓鱼网站的时候不会发现区别,钓鱼网站的页面会做的和正规页面一模一样,唯一的区别就是钓鱼网站内部有一个隐藏的input框去代替我们输入的对方账号的input框,最后向缴费服务端发送数据的时候,提交的是隐藏的数据。

    如何避免上述问题

    解决方案:csrf跨站请求伪造校验

    原理:当页面朝后端发送post请求的时候,会顺带发送一个唯一标识码,这个唯一标识码是在渲染页面的时候服务端给页面的数据,如果唯一标识不对,直接拒绝访问(403 forbbiden),如果成功则正常执行

    2 如何符合校验

    form表单符合校验代码

    <form action="" method="post">
        {% csrf_token %}
        <input type="submit">
    </form>
    

    ajax符合校验代码

    {% csrf_token %}
    <button id="d1">提交</button>
     
    // 方式三:推荐使用,导入一个写好的js文件去自动获取csrf
    {% load static %}
    <script src="{% static 'js/mysetup.js' %}"></script>
    <script>
        $('#d1').click(function () {
            $.ajax({
                url:'',
                type:'post',
                // 方式一:利用标签获取页面上的验证码
                data:{'username':'hz','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},
                // 方式二:利用模版语言便捷写法
                data:{'username':'hz','csrfmiddlewaretoken':{% csrf_token %}},
    
                data:{'username':'hz'},
                success:function () {
    
                }
            })
        })
    </script>
    
    

    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);
        }
      }
    });
    

    3 相关装饰器

    '''
    以下两种情况用到两种不同的装饰器
    1 网站整体都不校验csrf,就单独几个视图函数需要校验
    2 网站整体都不校验csrf,就单独几个视图函数不校验
    '''
    from django.views.decorators.csrf import csrf_protect,csrf_exempt
    '''
    csrf_protect:需要校验,在CBV种三种装饰器方法都可以使用
    csrf_exempt:忽视校验,在CBV中只能使用给dispatch加装饰器的方法
    '''
    
    @csrf_exempt
    @csrf_protect
    def index(request):
        print('我是index函数')
        # print(index)
        if request.method=='POST':
            print(request.POST.get('username'))
            return HttpResponse('post index')
        return render(request,'index.html')
    
    
    # CBV
    # @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')
    

    四、重要补充知识点

    补充

    # 通过一个模块实现和settins的中间件一样以字符串形式导入模块
    # 这个方法最小只能到py文件名
    import importlib
    res = 'myfile.b'
    # 下面等价于:from myfile import b
    ret = importlib.import_module(res)
    

    思想

    利用已学习的知识模仿django中间件导入

    # 创建一个包里面存有我们要使用的类
    
    # mymid.qq
    class QQ():
        def __init__(self):
            pass
    
        def send(self,msg):
            print(f'qq:{msg}')
    
    # mymid.wechat
    class Wechat():
        def __init__(self):
            pass
    
        def send(self,msg):
            print(f'wechat:{msg}')
            
    # 创建一个settings去存储这些类的路径
    # settings.py
    MYMID_DIR = [
        'mymid.qq.QQ',
        'mymid.wechat.Wechat',
    ]
    
    # 重点!!!!!!
    # mymid.__init__.py
    import importlib
    import settings
    
    def send_all(msg):
        for mymid in settings.MYMID_DIR:
            # 1 先用切片的方式获取模块路径和具体类名
            module_path,cls_name = mymid.rsplit('.',maxsplit=1)
            # print(module_path,cls_name)
            # 2 通过importlib模块以字符串的形式导入指定模块
            res = importlib.import_module(module_path)
            # 3 通过反射的方法获取类名
            cls = getattr(res,cls_name)
            obj = cls()
            # 4 利用鸭子类型直接调用send方法
            obj.send(msg)
            
            
    # start.py启动文件
    import mymid
    mymid.send_all('吃饭了')
    
  • 相关阅读:
    【DB宝50】Oracle异构平台迁移之完全可传输导出导入(Full Transportable Export & Import)
    【DB宝49】Oracle如何设置DB、监听和EM开机启动
    【DB宝48】JumpServer:多云环境下更好用的堡垒机
    【DB宝47】企业知识分享+团队协作神器之Confluence
    【DB宝46】NoSQL数据库之CouchBase简介、集群搭建、XDCR同步及备份恢复
    【DB宝45】MySQL高可用之MGR+Consul架构部署
    【DB宝44】Oracle rac集群中的IP类型简介
    【DB宝43】MySQL误操作闪回恢复利器之my2sql
    【DB宝42】MySQL高可用架构MHA+ProxySQL实现读写分离和负载均衡
    【DB宝41】监控利器PMM的使用--监控MySQL、PG、MongoDB、ProxySQL等
  • 原文地址:https://www.cnblogs.com/hz2lxt/p/13067622.html
Copyright © 2020-2023  润新知