Django之中间件
Posted mofujin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 装饰器
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‘ # ‘应用名.类名‘
1.执行数据库迁移命令
所有的auth模块功能 全部都基于你创建的表
而不再使用auth_user
怎么用
五.settings中的中间件原理 原型的思路 如何实现同时发送给不同功能函数 实现统一的接口的功能 类似鸭子类型
设计思想(****************) 插拔式
以上是关于Django之中间件的主要内容,如果未能解决你的问题,请参考以下文章