Django之中间件

  一.django中间件()

    1.是什么:django 中间件类似django 门户 保安

    请求的时候需要先经过中间件才能到达django 后端(urls,vies,templates,)

    响应走的时候也是需要经过中间件才能到达web服务网关接口

    django 中间件的默认七个门户

]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.com]mon.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,HttpResponse

class Mymidd1(MiddlewareMixin):
    def process_request(selfs, request):
        print('我是第一个中间件里面的process_request方法')
        # 注意:这里是从上到下执行
        return HttpResponse('OK')
    def process_response(self, request, response):
        print('我是第一个中间件里面的process_response响应方法')
        return response


class Mymidd2(MiddlewareMixin):
    def process_request(selfs, request):
        print('我是第二个中间件里面的process_request方法')
        # 注意:这里是从上到下执行

    def process_response(self, request, response):
        print('我是第二个中间件里面的process_response响应方法')

        return response


class Mymidd3(MiddlewareMixin):
    def process_request(selfs, request):
        print('我是第三个中间件里面的process_request方法')
        # 注意:这里是从上到下执行

    def process_response(self, request, response):
        print('我是第三个中间件里面的process_response响应方法')
        return response
我是第一个中间件里面的process_request方法

[25/Sep/2019 20:11:54] "GET /test1/ HTTP/1.1" 200 2
我是第一个中间件里面的process_response响应方法

如果在请求体中直接有相应结果 就会触发 process_response()  执同级别的process_request( ) 中额结果返回  必须通过process_response() 方法返回 return response 

    

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,HttpResponse

class Mymidd1(MiddlewareMixin):
    def process_request(selfs, request):
        print('我是第一个中间件里面的process_request方法')
        # 注意:这里是从上到下执行

    def process_response(self, request, response):
        print('我是第一个中间件里面的process_response响应方法')
        return response


class Mymidd2(MiddlewareMixin):
    def process_request(selfs, request):
        print('我是第二个中间件里面的process_request方法')
        # 注意:这里是从上到下执行
        return HttpResponse('OK')
    def process_response(self, request, response):
        print('我是第二个中间件里面的process_response响应方法')

        return response


class Mymidd3(MiddlewareMixin):
    def process_request(selfs, request):
        print('我是第三个中间件里面的process_request方法')
        # 注意:这里是从上到下执行

    def process_response(self, request, response):
        print('我是第三个中间件里面的process_response响应方法')
        return response

我们再次将

return HttpResponse('OK') 执行procses_request() 函数体中

我是第一个中间件里面的process_request方法
我是第二个中间件里面的process_request方法   # 也就是说只要请求中 我们拿到return Httpresponse('') 对象就不会再往下进行请求 通过process_response() 方法进行返回 但会执行同级别的request 请求 


我是第二个中间件里面的process_response响应方法
我是第一个中间件里面的process_response响应方法

总结:浏览器再向服务端发送请求时时通过中间件的执行顺序是从上 往下 一个 个 进行 执行 请求 数据 如果没有 再 往下一层 直到 process_request() 中的一层

返回了一个 HttpResponse  对象  而 拿到的结果 会往我们redis  存放一份数据  一份返回到浏览器 方便下次在进行同样的数据 请求 redis 可以直接 返回 减少访问数据库的压力

2.为什么

    (1)中间件的存在就是为了帮我们做检验 一次响应一次请求  如果数据不符合者直接在中间件就给你过滤,不会再走数据库 这样减少数据库的访问 减少数据库的压力  

    (2)网站全局的身份验证,访问频率的限制,权限的校验...要是涉及到全局的校验咱们都要可以在中间件取完成

    (3)django 的中间件是所有web框架中 做得最好的

    3.怎么做

    需要我们掌握的方法有

    1.process_request()方法

    2.process_response()方法

    需要了解的方法

   

要了解的方法
                        3.process_view()
                            1.在路由匹配成功执行视图函数之前 触发
                            
                        4.process_exception()
                            1.当你的视图函数报错时  就会自动执行
                            
                        5.process_template_response()
                            1.当你返回的HttpResponse对象中必须包含render属性才会触发
                            def index(request):
                                print('我是index视图函数')
                                def render():
                                    return HttpResponse('什么鬼玩意')
                                obj = HttpResponse('index')
                                obj.render = render
                                return obj

总结:

    

  二.基于中间件的解决跨站请求伪造

    1.钓鱼网站伪造

后端代码

def transfer(request):
    # 其他网站会向我们中国银行的网站访问一个页面 然后给用户输入 name='' 和 vlue= ;写的是我们自己的账号
    # 我们一旦提交 相当于向钓鱼网站进行转账 仿真页面
    # 真实网站
    if request.method == 'POST':
        username = request.POST.get('username')
        money = request.POST.get('money')
        to_name = request.POST.get('to_name')
        print(' %s向%s 转账%s '%(username,to_name,money))

    return render(request,'transfer.html')

前端代码

</head>
<body>
{#{% csrf_token %}#}
    <h1>中国银行</h1>
    <form action="" method="post">
        <p>username:<input type="text" name="username"></p>
        <p>money:<input type="text" name="money"></p>
#1 伪造的网站向中国银行请求一个一摸一样的页面
# 2伪造得的网站在input 框内做手脚 将用户提交的name='to_name'
属性去除 相当于 后端没办法识别

# 4 然后我们自己设置一个input 框 有name='to_name' 属性 将value=‘ccoo’ 目标账户设置为默认
        <p>to_name:<input type="text" name='to_name'></p>
        <input type="submit">
    </form>

</body>
</html>

伪造网站

def transfer(request):
    # 其他网站会向我们中国银行的网站访问一个页面 然后给用户输入 name='' 和 vlaue= ;写的是我们自己的账号
    # 我们一旦提交 相当于向钓鱼网站进行转账 仿真页面
    # 真实网站
    # 只需跳转页面即可
    return render(request,'transfer.html')

前端代码

{#{% csrf_token %}#}
    <h1>钓鱼网站</h1>
        这里就是向中国银行请求一个页面 然后开始设置input 框
    <form action=" http://127.0.0.1:8000/transfer/" method="post">
        <p>username:<input type="text" name="username"></p>
        <p>money:<input type="text" name="money"></p>
{#        不设置name属性#}
        <p>to_name:<input type="text"></p>
        <input type="text" name="to_name" value="hhh" style="display: none">
        <input type="submit">
    </form>

</body>
</html>

    2.如何解决:Django 跨站请求伪造

    直接在form表单 {% csrf_token%} 

   <form action="" method="post">
          {% csrf_token %}
        <p>username:<input type="text" name="username"></p>
        <p>money:<input type="text" name="money"></p>
        <p>to_name:<input type="text" name='to_name'></p>
        <input type="submit">
    </form>

      form 表单 {%csrf_token%}

      ajax 的三种处理csrf 的方法

      第一种ajax 提交form 表单 数据

   data:{'username':'koko','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},   注意 value 的来源 是form  的%csrf_token%{}表单
<body>
{#{% csrf_token %}#}
    <h1>中国银行</h1>

    <form action="" method="post">
          {% csrf_token %}
        <p>username:<input type="text" name="username"></p>
        <p>money:<input type="text" name="money"></p>
        <p>to_name:<input type="text" name='to_name'></p>
{#        <input type="submit" class="c1">#}
    </form>
<button class="c1">ajax提交</button>
</body>



<script>
    $('.c1').on('click',function () {
        $.ajax({
            url:'', // 路径这里有三种 类似:action 可以写全路径 默认不写诗当前地址
            type:'post',  //methon
            data:{'username':'koko','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},
            success:function (data) {
                alert(data)

            }
        })

    })


</script>
</html>

第二种

  data:{'username':'koko','csrfmiddlewaretoken':'{{ csrf_token }}'},   
</head>
<body> {#{% csrf_token %}#} <h1>中国银行</h1> <form action="" method="post"> {% csrf_token %} <p>username:<input type="text" name="username"></p> <p>money:<input type="text" name="money"></p> <p>to_name:<input type="text" name='to_name'></p> {# <input type="submit" class="c1">#} </form> <button class="c1">ajax提交</button> </body> <script> $('.c1').on('click',function () { $.ajax({ url:'', // 路径这里有三种 类似:action 可以写全路径 默认不写诗当前地址 type:'post', //methon {#data:{'username':'koko','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},#} data:{'username':'koko','csrfmiddlewaretoken':'{{ csrf_token }}'}, success:function (data) { alert(data) } }) }) </script> </html>

第三种

 在script 的上方直接引入

{% load static  %}
<script src="{% static 'ajax_js.js' %}"></script>
 // 第三种方式 :直接引入js文件
</head>
<body>
{#{% csrf_token %}#}
    <h1>中国银行</h1>

    <form action="" method="post">
          {% csrf_token %}
        <p>username:<input type="text" name="username"></p>
        <p>money:<input type="text" name="money"></p>
        <p>to_name:<input type="text" name='to_name'></p>
{#        <input type="submit" class="c1">#}
    </form>
<button class="c1">ajax提交</button>
</body>


{% load static  %}
<script src="{% static 'ajax_js.js' %}"></script>
<script>

    $('.c1').on('click',function () {
        $.ajax({
            url:'', // 路径这里有三种 类似:action 可以写全路径 默认不写诗当前地址
            type:'post',  //methon
            {#data:{'username':'koko','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},#}
            {#data:{'username':'koko','csrfmiddlewaretoken':'{{ csrf_token }}'},#}
            // 第三种方式 :直接引入js文件
            data:{'username':'koko'},
            success:function (data) {
                alert(data)

            }
        })

    })


</script>
</html>

  三.跨站请求伪造象关装饰器

如果是csrf_protect 那么有三种方式


1.当你网站全局都需要校验csrf的时候 有几个不需要校验该如何处理
2.当你网站全局不校验csrf的时候 有几个需要校验又该如何处理

from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt,csrf_protect
# 如果是接受跨站请求的保护 则用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')
    # AttributeError at /my_views/
    # 'MyView' object has no attribute 'COOKIES'
    # @csrf_protect 也就是说我将后端的中间件注释 掉他就不会 阻拦我 发送post 数据凡是我放这个保护的装饰器 就会将这个post
    # 阻拦数据传数
    # 这是第一种
    # @method_decorator(csrf_protect)
    def post(self,request):

        return HttpResponse('post')

如果是csrf_exempt 只有两种(只能给dispatch装)   特例

# @method_decorator(csrf_protect,name='post')   # 指名道姓
class MyView(View):
    # 这是第二种保护
    # 任务的分发

    # 不保护数据的安全习性
    # @method_decorator(csrf_exempt,name='dispatch')
    def dispatch(self, request, *args, **kwargs):
        res = super().dispatch(request,*args,**kwargs)
        return res
    
    def get(self,request):

        return HttpResponse('get')
    # AttributeError at /my_views/
    # 'MyView' object has no attribute 'COOKIES'
    # @csrf_protect 也就是说我将后端的中间件注释 掉他就不会 阻拦我 发送post 数据凡是我放这个保护的装饰器 就会将这个post
    # 阻拦数据传数
    # 这是第一种
    # @method_decorator(csrf_protect)
    # @method_decorator(csrf_exempt,name='post')
    # 放这里是不行的 起不到效果
    def post(self,request):

        return HttpResponse('post')

  四.auth模块

  注意事项:如果用了auto模块就必须用全套

  1.创建超级用户和普通用户

普通用户和超级用户的区别是 超级用户可以进行django 后台admin 的管理 

  2.创建普通用户

  普通用户

  增

from django.contrib.auth.models import User


def register(request):

    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')

        user_obj = User.objects.filter(username=username)
        if not user_obj:
        # 创建普通用户和超级用户
        # 注意:别再用create 的方法这里的是auth模块
            User.objects.create_user(username=username, password=password)  #  创建普通用户
            # User.objects.create_superuser(username=username, password=password)  # 这是创建超级用户
            return HttpResponse('注册成功')
    return render(request,'login.html')

  删

  用户注销

  

@login_required  # 无参装饰器
def logout(request):
    # request.session.flush()  # 没问题
    auth.logout(request)  # ok
    return HttpResponse('注销成功')

  改

# 装饰器
from django.contrib.auth.decorators import  login_required

# 修改密码
@login_required  #  # 自动校验当前用户是否登录  如果没有登录 默认跳转到 一个莫名其妙的登陆页面
def set_password(request):
    if request.method == 'POST':
        old_pwd = request.POST.get('old_pwd')
        print(old_pwd)
        new_pwd = request.POST.get('new_pwd')
        # 如何检验当前输入的密码和数据库的密码一致
        is_valid = request.user.check_password(old_pwd)
        # 会将当前输入的密码进行自动加密 然后去数据库中比对当前的用户的密码
        if is_valid:
            request.user.set_password(new_pwd)
            # 修改密码后一定记得save() 保存
            request.user.save()
            return HttpResponse('修改成功')

        else:
            return HttpResponse('密码有误')

  查 

    is_valid = request.user.check_password(old_pwd)
        # 会将当前输入的密码进行自动加密 然后去数据库中比对当前的用户的密码

  1.用户登录

def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user_obj = auth.authenticate(username=username,password=password)  # 必须用户auth 模块 因为数据库的密码是秘文
        if user_obj:
            # 保存sessioon 值
            auth.login(request,user_obj)  # 将用户的状态保存在session中
            #  """只要执行了这一句话  你就可以在后端任意位置通过request.user获取到当前用户对象"""
            return HttpResponse('登录成功')
        else:
            return HttpResponse('用户不存在')
    # 先返回一个页面
    return render(request,'register.html')

  

2.# 判断用户是否登录
def check_auth(request):
    print(request.user)  # koko
    print(request.user.is_authenticated)  # 判断用户是否登录CallableBool(True)
    return HttpResponse('ok')

  检验用户是否登陆 auth 装饰器

 1装饰器的局部设置 

@login_required(login_url='/login/')  # 如果没有登录 我们需要指定一个登陆的网址让他进行跳转 局部设置
那这样不是很麻分每个函数都的加
2.装饰器的全局设置
别急 我们有更好的方法 就是在settings 中 设置全局的LOGIN_URL = 'login
# auth登陆认证装饰器 跳转的url
LOGIN_URL = 'login'


# 告诉django  orm不再使用auth默认的表  而是使用你自定义的表
AUTH_USER_MODEL = 'app01.Userinfo'  # '应用名.类名'

  3.自定义userinfo 将不会在django 表中创建auth_user  可以增强拓展性 添加新字段

  如何做

自定义auth_user表
from django.contrib.auth.models import AbstractUser
# Create your models here.
# 第一种 使用一对一关系 不考虑

# 第二种方式 使用类的继承
class Userinfo(AbstractUser):
# 千万不要跟原来表中的字段重复 只能创新
phone = models.BigIntegerField()
avatar = models.CharField(max_length=32)

# 一定要在配置文件中 告诉django
# 告诉django orm不再使用auth默认的表 而是使用你自定义的表
AUTH_USER_MODEL = 'app01.Userinfo' # '应用名.类名'    # 注意了哈项目中的mdels  创建Userinfo(Abs) 继承我们数据库的表 必须在settings 中告诉django ORM 不在使用默认的表 而是使用咱们自定义的表


1.执行数据库迁移命令
所有的auth模块功能 全部都基于你创建的表
而不再使用auth_user

  怎么用

五.settings中的中间件原理 原型的思路 如何实现同时发送给不同功能函数 实现统一的接口的功能 类似鸭子类型 

设计思想(****************) 插拔式

六.序列化组件DRF 框架

    

原文地址:https://www.cnblogs.com/mofujin/p/11587092.html