zoukankan      html  css  js  c++  java
  • Django中间件

    Django中间件

    一、Django中间件简介

    ​ django中间件是类似于是django的保安,请求的时候需要先经过中间件才能到达django后端(urls,views,templates,models),响应走的时候也需要经过中间件才能到达web服务网关接口

    Django请求生命周期

    Django请求生命周期

    缓存数据库:当请求经过第一个中间件的时候,Django会去缓存数据库看看,当前请求资源是不是已经存在于缓存数据库,如果存在那么直接从缓存数据库中将资源拿出来返回给浏览器,就不走Django后端了,减轻了Django后端和数据库的压力;如果没有那么继续向Django后端请求,等拿到资源,向浏览器发送响应的时候,走到最后一个中间件时,会将拿到的资源在缓存数据库中存一份,然后再讲响应发送给浏览器。

    Django默认中间件有七个

    Django默认中间件

    这些字符串就是路径,我们可以通过模块的方式导入

    SecurityMiddleware则是一个类

    同理,SessionMiddleware等等也都是一个类

    观察这些类,发现这些类都继承了MiddlewareMixin,并且都有process_request方法

    Django中间件中有五个用户可以自定义的方法,需要我们掌握的方法有process_request()方法,process_response()方法,需要了解的方法有process_view()process_exception()process_template_response()

    Django中间件可以用来做什么

    ​ 1、网站全局的身份校验,访问频率限制,权限校验...只要是涉及到全局的校验都可以在中间件中完成

    ​ 2、django的中间件是所有web框架中做的最好的

    二、Django中间件需要掌握的两个方法

    ​ 首先我们需要自定义我们自己的中间件,在全局建一个文件夹,再建一个py文件,在py文件中写我们自己的中间件

    模仿Django源码的写法,我们写了三个自定义中间件,并在其中写了process_request方法

    然后我们需要去settings配置文件中注册我们写的中间件

    我们自定义的中间件在书写路径时是没有提示的,所以一定注意不能写错

    然后我们去写一个视图函数

    启动Django,在浏览器中访问该url

    观察终端打印的结果,发现中间件是在视图函数执行之前执行的,并且中间件的执行是有顺序的,按照在配置文件中书写的顺序从上往下执行

    然后我们在三个自定义中间件中定义process_response方法

    其余两个中间件类似(一定要返回response对象),定义完成之后,启动Django,访问url,得到如下结果

    可以看出process_response方法是在执行完视图函数之后执行的,并且process_response执行顺序与process_request相反,是按照settings配置文件中书写顺序从下往上执行的

    如果在第一个中间件返回一个HTTPResponse对象,会发生什么事呢?

    如果方法里面直接返回了HttpResponse对象,那么会直接返回不再往下执行,基于该特点就可以做访问频率限制,身份校验,权限校验

    而如果是flask框架,就不是在同级别的中间件返回,而是从最下面的中间件开始返回,依次执行process_response方法

    在process_response方法中,必须要将response对象返回,因为它指代的就是要返回给前端的数据

    三、Django中间件中需要了解的三个方法

    1、process_view方法

    先在三个中间件中定义peocess_view方法

    启动Django,访问url

    process_view方法是在执行视图函数之前执行,并且是在路由匹配成功后

    2、process_exception方法

    同样的定义process_exception方法

    访问url

    发现process_exception并没有执行

    当我们在视图函数中乱写两行之后

    浏览器报错,并且执行了process_exception方法

    总结:process_exception会在视图函数报错的时候执行

    3、process_template_response方法

    仍然在中间件中定义process_template_response方法

    注意到这里也返回了response对象,实际上只要形参有response就必须返回response,不然的话就相当于你借了别人东西却不还一样,会报错。

    并且将视图改为

    浏览器展示的内容是render属性加括号调用的结果

    总结:返回的对象中必须带有render属性才会执行

    四、csrf跨站请求伪造

    1、钓鱼网站

    钓鱼网站:通过制作一个跟正儿八经的网站一模一样的页面,骗取用户输入信息,转账交易,从而做手脚,转账交易的请求确确实实是发给了中国银行,账户的钱也是确确实实少了,唯一不一样的地方在于收款人账户不对。
    内部原理:在让用户输入对方账户的那个input上面做手脚,给这个input不设置name属性,在内部隐藏一个实现写好的name和value属性的input框,这个value的值就是钓鱼网站受益人账号。

    2、自己实现钓鱼网站

    3、防止钓鱼网站的思路

    ​ 网站会给返回给用户的form表单页面偷偷塞一个随机字符串,请求到来的时候会先比对随机字符串是否一致,如果不一致直接拒绝(403)

    该随机字符串有以下特点:同一个浏览器每一次访问都不一样,不同浏览器绝对不会重复

    4、防止钓鱼网站

    真正网站的form表单中只有三个p标签和一个input框

    而如果在表单中塞一个{% csrf_token %}

    ​ 真正的网站的form表单中就会多出一个隐藏的input框 。name属性标识它是一个csrf秘钥,value值是一个随机字符串,并且每发送一次请求这个随机字符串都是不一样的,不同的浏览器上的随机字符串绝对不会重复

    5、发送ajax请求时如何防止钓鱼网站

    1.现在页面上写{% csrf_token %},利用标签查找  获取到该input键值信息
    {'username':'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()}
    $('[name=csrfmiddlewaretoken]').val()
    				
    2.直接书写'{{ csrf_token }}'
    	{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'}
    	{{ csrf_token }}
    			
    3.你可以将该获取随机键值对的方法 写到一个js文件中,之后只需要导入该文件即可
    新建一个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);
            }
        }
    });
    // 当你写一个前后端分离的项目时,前端无法使用模板语法,就可以使用导入文件的方法
    

    6、跨站请求伪造相关装饰器

    当你把settings配置文件中间件中的csrf中间件打开时,所有向后端提交post请求的数据,都会被csrf校验,如果我们想让某一个或几个请求跳过校验该怎么办呢?当你网站全局不校验csrf的时候,有几个需要校验又该如何处理?

    这时我们就需要用到csrf_exemptcsrf_protect,见名知意,csrf_exempt是用来跳过校验的,而csrf_protect是用来在不校验的情况下进行校验的

    朝login发一个form表单的post请求,并没有带上csrf_token随机字符串,发现并没有校验

    而如果把装饰器去掉就会被校验

    同理csrf_protect的使用也是类似的

    在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_exempt 只有两种(只能给dispatch装)   特例
    	@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_exempt只能给dispatch装,而csrf_protect既可以给dispatch装,也可以给post

    装饰器中只有csrf_exempt是特例,其他的装饰器在给CBV装饰的时候都可以有三种方式

    五、auth模块方法大全

    1、auth模块有哪些功能?

    auth模块集成了和用户相关的功能,例如用户的注册,登录,验证,修改密码等等...

    auth_user表中的password字段是加密的,加密方式是sha256

    Django后台管理登录界面

    执行数据库迁移命令之后,会生成很多表,其中的auth_user是一张用户相关的表格(包含了超级用户即管理员和普通用户,利用is_superuser来区分)

    在run manage by task中输入createsuperuser 即可创建超级用户,这个超级用户就拥有登陆django admin后台管理的权限

    在run manage by task中创建超级用户可以不输入邮箱,它只会给你一个提示

    2、查询用户

    from django.contrib import auth
    user_obj = auth.authenticate(username=username,password=password)  
    # 必须要用这种方式因为数据库中的密码字段是密文的 而你获取的用户输入的是明文
    

    3、记录用户状态

    auth.login(request,user_obj)  # 将用户登录状态记录到django_session表中
    # 只要执行了这一句话,你就可以在后端任意位置通过request.user获取到当前用户对象
    # 如果没有执行这一句话,就通过request.user获取用户对象,那么拿到的是匿名用户AnonymousUser
    # 并且request.user.password获取用户密码时会报错
    

    这个随机字符串也会在你的浏览器存一份

    4、判断用户是否登录

    print(request.user.is_authenticated)  # 判断用户是否登录  如果是匿名用户会返回False
    

    5、用户登录之后 获取用户对象

    print(request.user)  # 如果没有执行auth.login那么拿到的是匿名用户
    

    6、校验用户是否登录

    from django.contrib.auth.decorators import login_required
    # 如果用户没有登录,那么它会跳到一个莫名其妙的页面,这种情况肯定是不允许发生的,
    # 所以我们必须指定它跳到我们写的登录页面
    @login_required(login_url='/xxx/')  # 局部配置
    def index(request):
        pass
    
    # 全局配置  settings文件中 
    # auth登录认证装饰器,跳转的URL
    LOGIN_URL = '/xxx/'
    

    7、验证密码是否正确

    request.user.check_password(old_password)
    

    8、修改密码

    request.user.set_password(new_password)
    request.user.save()  # 修改密码的时候 一定要save保存 否则无法生效
    

    9、退出登陆

    auth.logout(request)  # request.session.flush()
    

    10、注册用户

    from django.contrib.auth.models import User
    # 上面这句话就可以拿到Django自动为我们创建的auth_user表
    # User.objects.create(username =username,password=password)  # 创建用户名的时候 千万不要再使用create了,它会将密码以明文的形式存进表中
    # User.objects.create_user(username =username,password=password)  # 创建普通用户
    User.objects.create_superuser(username =username,password=password,email='123@qq.com')  # 创建超级用户  邮箱必填
    

    注意事项:

    如果你想用auth模块,那么你就用全套。

    auth.authenticate(username=username)这么写无效的,该方法不能只传一个参数。

    11、自定义用户表

    Django自动帮我们创建用户表的字段是固定的,如果我们想在用户表中添加几个字段,就必须要自己自定义用户表。

    思路:

    1.使用一对一关系,新建一张表加入新增字段与auth_user表关联(繁琐)

    2.使用类的继承,继承Django默认的用户表类,添加新的字段,已有的字段不需要书写,在默认表中就有,这种方式必须在同步数据库之前使用

    然后还必须在settings配置文件中告诉Django,我们不再用你默认的那张表了

    app01_userinfo表新增了两个字段,Django默认用户表的字段也全都有

    并且我们自定义的表拥有auth模块的所有功能

    六、模仿Django中间的思想用字符串添加功能

    1、架构

    2、start.py

    import notify
    
    notify.send_all('国庆放假了 记住放八天哦')
    

    3、settings.py

    NOTIFY_LIST = [
        'notify.email.Email',
        'notify.msg.Msg',
        # 'notify.wechat.WeChat',
        'notify.qq.QQ',
    ]
    

    4、notify/email.py

    class Email(object):
        def __init__(self):
            pass  # 发送邮件需要的代码配置
    
        def send(self,content):
            print('邮件通知:%s'%content)
    

    5、notify/msg.py

    class  Msg(object):
        def __init__(self):
            pass  # 发送短信需要的代码配置
    
        def send(self,content):
            print('短信通知:%s' % content)
    

    6、notify/qq.py

    class QQ(object):
        def __init__(self):
            pass  # 发送qq需要的代码准备
    
        def send(self,content):
            print('qq通知:%s'%content)
    

    7、notify/wechat.py

    class WeChat(object):
        def __init__(self):
            pass  # 发送微信需要的代码配置
    
        def send(self,content):
            print('微信通知:%s'%content)
    

    8、notify/__init__.py

    import settings
    import importlib
    
    
    def send_all(content):
        for path_str in settings.NOTIFY_LIST:  # 1.拿出一个个的字符串   'notify.email.Email'
            module_path,class_name = path_str.rsplit('.',maxsplit=1)  # 2.从右边开始 按照点切一个 ['notify.email','Email']
            module = importlib.import_module(module_path)  # from notity import msg,email,wechat
            cls = getattr(module,class_name)  # 利用反射 一切皆对象的思想 从文件中获取属性或者方法 cls = 一个个的类名
            obj = cls()  # 类实例化生成对象
            obj.send(content)  # 对象调方法
    
  • 相关阅读:
    having——至少被订购过两回的订单
    产品——仓库表查询
    SQL 聚集函数使用
    select count(*)和select count(1)的区别 (转)
    SpringAOP 通知(advice)
    Spring AOP 实现原理与 CGLIB 应用
    cglib 动态代理基础篇
    模仿Struts2的Interceptor拦截器实现
    利用JDK动态代理机制实现简单拦截器
    java多线程总结二:后台线程(守护线程)
  • 原文地址:https://www.cnblogs.com/DcentMan/p/11588258.html
Copyright © 2011-2022 走看看