共计 8554 个字符,预计需要花费 22 分钟才能阅读完成。
引入
1. 什么是中间件
中间件是一个很大的概念, 它介于两个事务之间
- 服务器中间件:服务器的调优,例:Java 的 Tomcat
- 消息队列中间件:消息队列,在应用程序与应用程序之间,
- 数据库中间件:应用程序与数据库之间
一.Django 中间件 (middleware)
1. 什么 Django 中间件
- 请求来的时候需要先经过中间件才能到真正的 Django 后端
- 响应走的时候也需要经过中间件才能发送出去
- 通俗的讲 : 中间件相当于是 Django 的门户, 你进来时要经过它, 出去的时候也要经过它
- 介于 request 与 response 处理之间的一道处理过程, 并且在全局上改变 django 的输入与输出
2.Django 自带的中间件
- Django 自带的中间件有七个, 在 setting.py 配置文件中, 每个中间件其实就是一个类
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', # 处理 session
'django.middleware.common.CommonMiddleware', # 处理路由匹配是否带斜杠
'django.middleware.csrf.CsrfViewMiddleware', # 跨站请求伪造处理
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
3. 中间件的作用
- 既然请求来和响应走都经过中间件, 那么我们就可以对所有的请求做一些预处理, 对所有返回的响应也可以做处理
- 每一个中间件都有具体的功能
4. 中间件的主要方法介绍
- 可以通过查看 Django 自带中间件的源码查看他们一般都有什么方法 : (五种)
中间件方法 | 描述 |
---|---|
process_request(self,request)(常用) | 所有请求来时都会运行的方法 |
process_view(self, request, callback, callback_args, callback_kwargs) | 所有路由匹配成功之后,跳转执行视图函数之前都会运行该方法 |
process_exception(self, request, exception) | 所有视图中有异常发生时运行的方法 |
process_response(self, request, response)(常用) | 所有返回页面响应时运行的方法 |
process_template_response(self,request,response) | 返回的 HttpResponse 对象具有 render 属性时才会触发该方法 |
ps : 并不是每个中间件都有这五个方法, 只是需要什么方法就有什么方法
-
process_request(), process_response() 重点
process_request
1. 请求来的时候会依此走 MIDDLEWARE 中定义的该方法, 如果中间件没有定义就跳过
- 执行顺序: MIDDLEWARE 中从上到下
2. 如果返回 HttpResponse 对象, 请求将不会继续往后执行, 而是原路返回
- 目的: 用来做全局相关的所有限制功能
- 应用: 校验失败不允许访问
process_response
1. 响应走的时候会依此走 MIDDLEWARE 中定义的该方法, 如果中间件没有定义就跳过
- 执行顺序: MIDDLEWARE 中从下到上
2. 该方法必须返回 HttpResponse 对象
- 默认返回形参 response
- 返回自定义的
-
process_view(), process_template_response(), process_exception() 了解
process_view
- 路由匹配之前或者执行视图函数之前时触发
- 执行顺序: MIDDLEWARE 中从上到下
process_template_response
- 视图函数返回结果中含有 render 属性是触发
- 执行顺序: MIDDLEWARE 中从下到上
process_exception
- 视图函数出现异常时触发
- 执行顺序: MIDDLEWARE 中从下到上
5. 自定义中间件流程
- 首先要知道中间件的执行顺序
1. 请求来时, 自上而下顺序执行
2. 响应走时, 自下而上顺序执行
- 自定义中间件步骤
1. 先在项目名目录或者应用名目录下创建一个任意名字的文件夹(最好见名知意)
2. 在该文件夹下创建任意名字的 py 文件(最好见名知意)
3. 在该 py 文件内书写类, 在类中书写中间件的五种方法
- 类必须继承 MiddlewareMixin, 五种方法并不是全都要写, 需要几种写几种
4. 将类的路径以字符串的形式注册到 settings.py 配置文件中的 MIDDLEWARE 下(与 app 注册原理类似)
6. 自定义中间件操作
- 创建文件夹和 py 文件, 再书写类
from django.utils.deprecation import MiddlewareMixin # 导入 Middleware 类
from django.shortcuts import HttpResponse, redirect, render
class FirstMiddleware(MiddlewareMixin):
def process_request(self, request):
print(" 请求来了 11111")
def process_response(self, request, response):
print(" 响应走了 11111")
return response # 必须返回 response
class SecondMiddleware(MiddlewareMixin):
def process_request(self,request):
print(" 请求来了 22222")
def process_response(self,request,response):
print(" 响应走了 22222")
return response # 必须返回 response
- 到 setting.py 中的 MIDDLEWARE 配置中去注册
# 一字符串的形式添加自定义中间件的路径
'app01.middleware.mymiddle.FirstMiddleware',
'app01.middleware.mymiddle.SecondMiddleware',
注意上面两个中间件的顺序是 First 在上, Second 在下
- 启动项目发起任意请求查看输出
- 如果我们在 First 中的 process_request 方法中添加 返回值 HttpResponse 对象
class FirstMiddleware(MiddlewareMixin):
def process_request(self, request):
print(" 请求来了 11111")
return HttpResponse('1') # 添加 HttpResponse 返回值
....
- 再查看实验效果
First 中 process_request 有返回 HttpResponse 对象, 那么接下来的中间件都不执行, 直接原路返回
7. 自定义中间件示例
-
统计所有登入用户的用户名、ip、时间、平台、客户端类型
-
中间件 mymiddle.py 文件
from django.utils.deprecation import MiddlewareMixin
from app01 import models
class MyMiddleware(MiddlewareMixin):
def process_request(self, request):
name = request.POST.get('name')
if name is not None:
IP = request.META.get('REMOTE_ADDR')
OS = request.META.get('OS').split('_')[0]
client = request.META.get('HTTP_USER_AGENT').split('')[-1].split('/')[0]
if client == 'Safari':
client = 'Chrome'
models.Visits.objects.create(name=name, IP=IP, OS=OS, client=client)
def process_response(self, request, response):
return response
def process_view(self, request, view_name, *args, **kwargs):
print("")
- 在 setting.py 中注册
'app01.middleware.mymiddle.MyMiddleware',
- 路由层 urls.py 文件
# 访问统计
path('', views.visit_login),
re_path('^visit_data/', views.visit_data),
- 视图层 views.py 文件
# 访问统计登入页面
def visit_login(request):
if request.method == "GET":
return render(request, 'visit_login.html')
elif request.method == "POST":
return render(request, 'visit_login.html')
# 访问统计页面(分页模板)
from app01 import models
from django.core.paginator import Paginator
def visit_data(request):
# 🔰1. 分页后的 paginator 对象
current_page = int(request.GET.get('page_num', 1))
# 🔰2. 页码列表
if paginator.num_pages > 9:
if current_page - 4 < 1:
page_range = range(1, 10)
elif current_page + 4 > paginator.num_pages:
page_range = range(paginator.num_pages - 8, paginator.num_pages + 1)
else:
page_range = range(current_page - 4, current_page + 4)
else:
page_range = paginator.page_range
# 🔰3.page 对象
try:
page = paginator.page(current_page)
except Exception as E:
page = paginator.page(current_page)
return render(request, 'visit_data.html', {'page_range': page_range, 'page': page, 'current_page': current_page})
- 演示效果(没有做登入校验)
二.CSRF 跨站请求伪造
1. 什么是 CSRF (简介)
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者 Session Riding,通常缩写为 CSRF 或者 XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与 XSS 非常不同,XSS 利用站点内的信任用户,而 CSRF 则通过伪装来自受信任用户的请求来利用受信任的网站。与 XSS 攻击相比,CSRF 攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比 XSS 更具危险性
- 简单理解就是黑客盗用你的身份, 以你的身份来进行一系列操作(发送恶意请求, 发消息, 购物, 转账等等), 这些操作对于服务器来说是完全合法的, 因为你的身份就是一个正常的用户
2.CSRF 攻击原理示例
- 以银行转账为例
- 正常用户登入受信任的网站 A, 并在本地生成 Cookie
- 在没有登出网站 A 的情况下(也就是没有清除 Cookie), 访问了黑客网站 B
- B 网站中有一个虚假按钮, 背后对应的就是向别人转账, 但用户并不知道, 点击之后会带着用户的 Cookie 进行转账
3.CSRF 攻击防范之 Referer
- 验证 HTTP Referer 字段
在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址, 服务器只要校验来源地址是不是自己的地址, 如果来源地址是其他网站的域名, 则很有可能是黑客的 CSRF 攻击, 直接拒绝该请求
- 优缺点
优点 : 简单, 易于实现
缺点 : Referer 的值是浏览器提供的, 我们不能保证浏览器自身的漏洞, 这样依赖于第三方的校验显然是不安全的, 并且某些浏览器目前已经有一些方法可以篡改 Referer 值, 比如 IE6 或 FF2
4.CSRF 攻击防范之 token
- 验证 token 随机字符串
在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求
三. 针对 from 表单 及 ajax 的 csrf_token 校验提交
1.CSRF 校验的中间件
- 在 setting.py 中开启, 默认是打开的
'django.middleware.csrf.CsrfViewMiddleware',
2. 在前端进行请求
- 如果没有携带 csrf 随机码,
POST
将会请求失败(403)
3. 在页面 form 表单中加上 {% csrf_token %}
(这是方法之一)
<form action="/csrf_test/" method="post">
{% csrf_token %}
....
....
</form>
添加之后会在页面上生成一个隐藏的 input 标签, 里面的 name 对应
csrfmiddlewaretoken
, value 对应一串随机码, 请求发送的时候就回去校验这个随机码这个时候进行 POST 请求就可以成功了
<input type="hidden" name="csrfmiddlewaretoken" value="Fuih8hBFfSw7usOBVz1FF8yWYlBVeqmJ59O0HGGZp9Rko4Ovm9F5QnS2Zm0dKV5K">
4. 基于 from 表单提交 CSRF 请求
- 实现的方法就是上面介绍的, 在 form 表单中添加
{% csrf_token %}
5. 基于 ajax 提交 CSRF 请求
- 方式一 (1): 在 data 中放入 csrf 随机字符串
# 首先需要在 form 表单中写上 {% csrf_token %},(不然后面取不到)
# 然后直接通过 jQuery 语法获取到页面中隐藏的 input 框内的 name 和 value, 将其放入 data 中
data:{'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()}
- 方式一 (2)
# 直接拿到 csrf_token 值进行渲染, 需要引号(不需要在 form 表单中写 csrf_token)
data:{'csrfmiddlewaretoken':'{{ csrf_token}}'}
- 方式二(1、2) : 将其放在请求头中
# 需要在 form 表单中写上 {% csrf_token %}
headers:{'X-CSRFToken':$('[name="csrfmiddlewaretoken"]').val()}
# 或者直接使用 '{{csrf_token}}' 取值进行渲染
headers:{'X-CSRFToken':'{{ csrf_token}}'}
6.CSRF 校验示例
- 路由层 urls.py 文件
# csrf 跨站请求伪造测试
re_path('^csrf_test/',views.csrf_test),
- 视图层 views.py 文件
from django.shortcuts import render, HttpResponse, redirect
def csrf_test(request):
if request.method == 'GET':
return render(request,'csrf_test.html')
elif request.method == "POST":
from_name = request.POST.get('from_name')
to_name = request.POST.get('to_name')
money = request.POST.get('money')
return HttpResponse(f'{from_name}向 {to_name} 转账 {money} 元成功')
- 模板层 csrf_test.html 文件
<div class="container-fluid">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-default">
<div class="panel-heading text-center">
<h3 class="panel-title"> 转账 </h3>
</div>
<div class="panel-body">
<form action="/csrf_test/" method="post">
{% csrf_token %} {# 方式一: 直接导入 csrf_token #}
<p> 你的账户名: <input type="text" name="from_name" class="form-control b1"></p>
<p> 对方账户名: <input type="text" name="to_name" class="form-control b2"></p>
<p> 转账金额: <input type="text" name="money" class="form-control b3"></p>
<input type="submit" value="form 转账 " class="btn btn-block btn-warning">
</form>
<br>
<input type="submit" class="btn btn-danger btn-block a1" value="Ajax 转账 ">
</div>
</div>
</div>
</div>
</div>
<script>
$('.a1').click(function () {
$.ajax({
url:'/csrf_test/',
method:'post',
{# 方式二 : 请求体中放随机字符串, 两种方式放 #}
{# 第一种: 直接通过页面获取 #}
{#data:{'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val(),'from_name':$('.b1').val(),'to_name':$('.b2').val(),'money':$('.b3').val()},#}
{# 第二种 : 直接拿到随机字符串渲染好, 需要加引号 #}
data:{'csrfmiddlewaretoken':'{{ csrf_token}}','from_name':$('.b1').val(),'to_name':$('.b2').val(),'money':$('.b3').val()},
{# 方式三 : 在请求头上加 #}
{#headers:{'X-CSRFToken':$('[name="csrfmiddlewaretoken"]').val()},#}
success:function (data) {alert(data)
}
})
})
</script>
- 我们先不添加 csrf 来进行试验
- 添加 csrf 随机码来进行校验
四.CSRF 校验局部禁用与局部使用
1. 两种装饰器
@csrf_exempt
: 全局启用 CSRF 校验的时候, 使用该装饰器可以使得局部不进行校验@csrf_protect
: 全局禁用 CSRF 校验的时候, 使用该装饰器可以使得局部仍然进行校验
2. 两种装饰器的使用示例
- 先导入装饰器
from django.views.decorators.csrf import csrf_exempt,csrf_protect
- 为 CBV 添加装饰器
# @csrf_exempt # 全局启用,csrf_CBV 可以不进行校验
@csrf_protect # 全局禁用(也就是注释掉 CSRF 校验的中间件),csrf_CBV 仍然进行校验
def csrf_CBV(request):
if request.method == 'GET':
return render(request,'csrf_CBV.html')
elif request.method == "POST":
from_name = request.POST.get('from_name')
to_name = request.POST.get('to_name')
money = request.POST.get('money')
return HttpResponse(f'{from_name}向 {to_name} 转账 {money} 元成功')