【2020Python修炼记】web框架之Django 中间件

【目录】

一、Django 中间件

1、中间件的介绍

2、自定义中间件

二、csrf跨站请求伪造

1、为何要使用-csrf跨站请求伪造

2、如何符合校验

3、scrf 相关装饰器

三、补充知识-模块 importlib

四、基于Django 中间件的一个重要编程思想

一、Django 中间件

  1、中间件的介绍

【2020Python修炼记】web框架之Django 中间件

#【什么是 Django 中间件】

Django 中间件是Django的门户——请求 与 响应 的传输,都需要经过 中间件。

1.请求来的时候需要先经过中间件才能到达真正的django后端
2.响应走的时候最后也需要经过中间件才能发送出去

# 【组成】

Django 自带七个 中间件( SecurityMiddleware / SessionMiddleware / CommonMiddleware /  CsrfViewMiddleware

AuthenticationMiddleware /  MessageMiddleware / XFrameOptionsMiddleware ),

每个中间件 都有各自的功能;且支持程序员 自定义中间件。

在 Django 框架的settings文件里,可以看到中间件的组成:

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)配置文件里的七个中间件,都是 字符串格式的模块路径(由 模块路径+模块名 组成)

例如
‘django.middleware.security.SecurityMiddleware‘ ——

from django.middleware.security import SecurityMiddleware

(2)源码

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
        
        process_response
    2.了解即可
        process_view
            
        process_template_response
        
        process_exception
"""

# 【怎么使用】

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

  • 全局用户身份校验

  • 全局用户权限校验(补充)

  • 全局访问频率校验

  • ...

  2、自定义中间件

 【自定义中间件的步骤】

(1)【创建文件夹】在项目名或者应用名下创建一个任意名称的文件夹

(2)【创建python文件】在该文件夹内创建一个任意名称的py文件

(3)【书写类方法】在该py文件内需要书写 类,

(这个类必须继承MiddlewareMixin — 因此 需要通过中间件源码找到 MiddlewareMixin 的模块路径,并导入模块。)

然后在这个类里面就可以自定义五个方法了(这五个方法并不是全部都需要书写,用几个写几个)

from django.utils.deprecation import MiddlewareMixin

【2020Python修炼记】web框架之Django 中间件

(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‘,
    ‘你自己写的中间件的路径2‘,
    ‘你自己写的中间件的路径3‘,
]【#  在应用下 自定义中间件,写路径时会有提示(如果在项目目录下直接自定义中间件,则没有提示)
# 在应用下 自定义中间件 路径格式: 应用名.文件夹名.python文件名.自定义类名】

【中间件分类(必须掌握和了解即可)-自定义举例】

(1)【必须掌握】

process_request
1.请求来的时候需要经过每一个中间件里面的process_request方法
  结果的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
2.如果当前中间件里面没有定义该方法,那么直接跳过执行下一个中间件
3.如果该方法返回了 HttpResponse对象,那么请求将不再继续往后执行
  而是直接原路返回(校验失败不允许访问...)
process_request 方法就是用来做全局相关的所有限制功能(例如:各种验证,反爬功能)

process_response
1.响应走的时候需要经过每一个中间件里面的 process_response 方法
  该方法有两个额外的参数request,response
2.该方法必须返回一个HttpResponse对象
  1.默认返回的就是 形参response
  2.你也可以自己返回自定义的内容
  3.顺序是按照配置文件中注册的中间件从下往上依次经过,如果你没有定义的话 直接跳过执行下一个

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

而只会直接走同级别的process_reponse返回

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

【2020Python修炼记】web框架之Django 中间件

from django.utils.deprecation import MiddlewareMixin


class md(MiddlewareMixin):    # 定义类 (继承 MiddlewareMixin)
    def process_request(self,requwest):    # 请求的方法,需自带一个request形参
        print("第一个中间件的 process_request ")

    def process_response(self,request,response):   # 响应的方法,自带两个形参 request,response
        print("第一个中间件的 process_response ")
        return response  # 需要返回response ,否则会报错

自定义中间件的类以及中间件方法

(2)【了解即可】

process_view
路由匹配成功之后 执行视图函数之前,会自动执行中间件里面的该方法
顺序是按照配置文件中注册的中间件从上往下的顺序依次执行

process_template_response
返回的HttpResponse对象有render属性的时候才会触发
顺序是按照配置文件中注册了的中间件从下往上依次经过

process_exception
当视图函数中出现异常的情况下触发
顺序是按照配置文件中注册了的中间件从下往上依次经过

 

二、csrf跨站请求伪造

1、为何要使用-csrf跨站请求伪造

"""
钓鱼网站
我搭建一个跟正规网站一模一样的界面(中国银行)
用户不小心进入到了我们的网站,用户给某个人打钱
打钱的操作确确实实是提交给了中国银行的系统,用户的钱也确确实实减少了
但是唯一不同的时候打钱的账户不适用户想要打的账户变成了一个莫名其妙的账户

大学英语四六级
考之前需要学生自己网站登陆缴费

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

如何规避上述问题
csrf跨站请求伪造校验
网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识
当这个页面朝后端发送post请求的时候 我的后端会先校验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden)如果成功则正常执行
"""

  2、如何符合校验

# 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‘}
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、scrf 相关装饰器

"""
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符合我们之前所学的装饰器的三种玩法
csrf_exempt   忽视校验
    针对csrf_exempt只能给dispatch方法加才有效
"""
# @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‘)



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

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

四、基于Django 中间件的一个重要编程思想 

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)

相关推荐