django中间键

 


一 django中间件

  django中间件是django的门户

  1.请求来的时候需要先经过中间件才能到达真正的django后端

  2.响应走的时候最后也需要经过中间件才能发送出去

  django自带七个中间件,每个中间件都有各自对应的功能,并且django还支持程序员自定义中间件

  你在用django开发项目的项目的时候,只要是涉及到全局相关的功能都可以使用中间件方便的完成

  eg:全局用户身份校验,全局用户权限校验(补充),全局访问频率校验

1.1 七个中间件

 
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 自定义中间件

  django支持程序员自定义中间件并且暴露给程序员五个可以自定义的方法

  1.必须掌握:process_request,process_response

  2.了解即可:process_view,process_template_response,process_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',
]
 

  process_request

  1.请求来的时候需要经过每一个中间件里面的process_request方法,结果的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行

  2.如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件

  3.如果该方法返回了HttpResponse对象,那么请求将不再继续往后执行,而是直接原路返回(校验失败不允许访问...)

  process_request方法就是用来做全局相关的所有限制功能

   

 1 from django.utils.deprecation import MiddlewareMixin
 2 from django.shortcuts import HttpResponse
 3 
 4 class Mymoddle(MiddlewareMixin):  #自定义中间件   记得注册
 5     def process_request(self,request):
 6         print('我是第一个process_request中间键')
 7         return HttpResponse('提前返回了')
 8 
 9     def process_response(self,request,response):    #需要哪个定义那个但是记得response需要把截取到的response返回回去(不然报错必须要)
10         print('我是中间键一的process_response')
11         return response
12 
13 
14 class Mymoddle2(MiddlewareMixin):  #自定义中间件   记得注册
15     def process_request(self,request):
16         print('我是第二个process_request中间键')
17 
18     def process_response(self,request,response):    #需要哪个定义那个但是记得response需要把截取到的response返回回去(不然保存)
19         print('我是中间键二的process_response')
20         return HttpResponse('狸猫换太子')
21 #flask 只要返回了就必须经过每一个process_response
自定义中间键写法

 

 

  process_response

  1.响应走的时候需要结果每一个中间件里面的process_response方法,该方法有两个额外的参数request,response

  2.该方法必须返回一个HttpResponse对象:

    1.默认返回的就是形参response

    2.你也可以自己返回自己的

  3.顺序是按照配置文件中注册了的中间件从下往上依次经过,如果你没有定义的话 直接跳过执行下一个

 

  研究

  如果在第一个process_request方法就已经返回了HttpResponse对象,那么响应走的时候是经过所有的中间件里面的process_response还是有其他情况

  是其他情况:就是会直接走同级别的process_reponse返回

 

 

  flask框架也有一个中间件但是它的规律:只要返回数据了就必须经过所有中间件里面的类似于process_reponse方法

  

  process_view

  路由匹配成功之后执行视图函数之前,会自动执行中间件里面的该放法

  顺序是按照配置文件中注册的中间件从上往下的顺序依次执行

 

  process_template_response

  返回的HttpResponse对象有render属性的时候才会触发

  顺序是按照配置文件中注册了的中间件从下往上依次经过

 

  process_exception

  当视图函数中出现异常的情况下触发

  顺序是按照配置文件中注册了的中间件从下往上依次经过

 
#自定义的中间件
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)





#setting.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'
]




#vivew.py
def index(request):
    print('我是视图函数index')
    obj = HttpResponse('index')
 
    def render():
        print('内部的render')
        return HttpResponse("O98K")
    obj.render = render
    return obj
 

 

二 csrf跨站请求伪造

  钓鱼网站是什么:我搭建一个跟正规网站一模一样的界面(中国银行),用户不小心进入到了我们的网站,用户给某个人打钱,打钱的操作确确实实是提交给了中国银行的系统,

          用户的钱也确确实实减少了,但是唯一不同的时候打钱的账户不适用户想要打的账户变成了一个莫名其妙的账户

  内部本质:我们在钓鱼网站的页面 针对对方账户 只给用户提供一个没有name属性的普通input框,然后我们在内部隐藏一个已经写好name和value的input框

  如何规避上述问题:csrf跨站请求伪造校验,网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识,当这个页面朝后端发送post请求的时候

           我的后端会先校验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden)如果成功则正常执行  

2.1如何符合校验

  在之前使用post提交数据的时候我们都要注释掉下面这句话,这句话其实就是控制是否需要校验的

'django.middleware.csrf.CsrfViewMiddleware',

  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 }}'}
#第三种 通用方式直接拷贝js代码并应用到自己的html页面上即可
data:{"username":'jason'}
 使用代码如下
    {% load static %}<!--使用前需要先加载-->
    <script src="{% static 'js/myajaxtoken.js' %}"></script>  <!--加载静态文件 ajasx就不用添加csrf_token--></head>
<body>
<div>{#    真网址#}
{#    {% csrf_token %}#}
    账号:<input type="text" name="username">
    密码:<input type="text"  >
    <input type="hidden" name="money" value="4658987">
    <button>点击提交post请求</button>

<button id="d1" >请点击</button></div>

<script>//写ajax一定要谨慎很容易出错
    $('#d1').click(function(){

            $.ajax({
            url:'',
            type:'post',
            {#data:{'username':'taoge','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},#}//这是获取这里已近定义过的{#{% csrf_token %}#}太复杂 ①
            data:{'username':'taoge','csrfmiddlewaretoken':'{{ csrf_token }}'},②因为前后端分离可能无法使用模板语法

success:function (args) {
            }

        })
    })
</script>
</body>

 

创建静态文件夹 js文件夹 py文件名任意 配置好静态文件 加载到前端进行使用 此代码来只官网(官网的解决方案)并非个人所写 

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);
    }
  }
});
ajax校验post权限请求文件(第三种方式js文件)

2.2 csrf相关装饰器(直接在views加装饰器即可FBV正常添加CBV有点区别)

  1.网站整体都不校验csrf,就单单几个视图函数需要校验
  2.网站整体都校验csrf,就单单几个视图函数不校验

from django.views.decorators.csrf import csrf_protect,csrf_exempt    本体模块
#csrf_protect  需要校验:针对csrf_protect符合我们之前所学的装饰器的三种玩法
#csrf_exempt  忽视校验:针对csrf_exempt只能给dispatch方法加才有效
 需要用到 CBV装饰模块 from django.utils.decorators import method_decorator     前知识模块
from django.views import View

# @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')
 

三 补充知识点:通过字符串的方式导入模块 

 
# 模块:importlib
import importlib
res = 'myfile.b'
ret = importlib.import_module(res) 如同: # from myfile import b
# 该方法最小只能到py文件名
print(ret)
 

 

 

  重要思想及案例

  创建一个notify的文件夹在改文件夹下根据功能的不同创建不同的py文件

 

 

 

#qq.py
class QQ(object):
    def __init__(self):
        pass  # 发送qq需要做的前期准备工作
 
    def send(self, content):
        print('qq通知:%s' % content)


#wechat.py
class Wechat(object):
    def __init__(self):
        pass  # 发送微信需要做的前期准备工作
 
    def send(self,content):
        print('微信通知:%s'%content)
 

   在应用下创建一个settings.py文件,在里面写上(需要不执行直接注释即可 需要添加 添加相应地址 创建相应的文件定义相同的鸭子类)此方法的研究目的

NOTIFY_LIST = [
    'notify.qq.QQ',
    'notify.wechat.Wechat',
]

  在notify的文件夹下创建__init__.py文件

 
import settings
import importlib

def send_all(content):
    for path_str in settings.NOTIFY_LIST:  #'notify.email.Email'
        module_path,class_name = path_str.rsplit('.',maxsplit=1)
        # module_path = 'notify.email'  class_name = 'Email'
        # 1 利用字符串导入模块
        module = importlib.import_module(module_path)  # from notify import email
        # 2 利用反射获取类名
        cls = getattr(module,class_name)  # Email、QQ、Wechat
        # 3 生成类的对象
        obj = cls()
        # 4 利用鸭子类型直接调用send方法
        obj.send(content)
 

   在应用下创建一个start.py文件,在里面写上

import notify
notify.send_all('快下课了')

  这样就实现了和Django中用字符串导入模块的功能了

posted @ 2021-05-30 17:04  欧阳锦涛  阅读(102)  评论(0)    收藏  举报
TOP 底部