Django中间件

Django中间件

一、Django中间件简介

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

Django请求生命周期

Django请求生命周期

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

Django默认中间件有七个

Django默认中间件

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

1727302-20190925171903066-413015213.png

SecurityMiddleware则是一个类

1727302-20190925172101441-1488581373.png

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

1727302-20190925172349225-1121307340.png

1727302-20190925172456734-1111892327.png

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

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

Django中间件可以用来做什么

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

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

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

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

1727302-20190925185649363-1895543682.png

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

1727302-20190925185456129-1951451713.png

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

1727302-20190925190129280-643757590.png

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

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

1727302-20190925190445076-268051085.png

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

1727302-20190925190626603-715305401.png

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

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

1727302-20190925191203065-289247434.png

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

1727302-20190925191401578-566638599.png

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

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

1727302-20190925194644263-614622395.png

1727302-20190925194804242-1870391075.png

1727302-20190925194844785-836594332.png

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

1727302-20190925195228010-1841554274.png

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

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

1727302-20190925195812311-26597613.png

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

1、process_view方法

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

1727302-20190925200115383-186591828.png

启动Django,访问url

1727302-20190925200403573-425703056.png

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

2、process_exception方法

同样的定义process_exception方法

1727302-20190925200756559-1427941889.png

访问url

1727302-20190925200905548-317757603.png

发现process_exception并没有执行

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

1727302-20190925201027539-1281737322.png

1727302-20190925201138893-657434719.png

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

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

3、process_template_response方法

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

1727302-20190925201734368-1238263570.png

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

并且将视图改为

1727302-20190925201635439-975819173.png

1727302-20190925202012785-360598583.png

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

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

四、csrf跨站请求伪造

1、钓鱼网站

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

2、自己实现钓鱼网站
3、防止钓鱼网站的思路

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

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

4、防止钓鱼网站

1727302-20190925203343798-944738813.png

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

1727302-20190925203612582-1431594223.png

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

1727302-20190925203834736-908057938.png

​ 真正的网站的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的时候,有几个需要校验又该如何处理?

1727302-20190925210058249-831427927.png

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

1727302-20190925210444460-1482434849.png

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

1727302-20190925210727508-1206999103.png

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

同理csrf_protect的使用也是类似的

在CBV中又如何给视图函数加装饰器呢?

1727302-20190925211300266-1786251802.png

这么装行不行呢?

1727302-20190925211347991-302742438.png

结果是不行的,那该怎么装呢?

# 如果是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模块集成了和用户相关的功能,例如用户的注册,登录,验证,修改密码等等...

1727302-20190925215859219-1396559519.png

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

Django后台管理登录界面

1727302-20190925220135115-1944887047.png

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

1727302-20190925215102766-1903649303.png

在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获取用户密码时会报错

1727302-20190925221534243-632624211.png

1727302-20190925220724405-1333823357.png

1727302-20190925220814492-1836121976.png

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

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默认的用户表类,添加新的字段,已有的字段不需要书写,在默认表中就有,这种方式必须在同步数据库之前使用

1727302-20190925224524775-2127035955.png

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

1727302-20190925224415854-1230035947.png

1727302-20190925224636664-719238923.png

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

1727302-20190925224740037-1314312834.png

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

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

1、架构

1727302-20190925225948235-979842181.png

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)  # 对象调方法

转载于:https://www.cnblogs.com/DcentMan/p/11588258.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值