Django 基于中间件实现 IP 访问频率限制详解
本文详细介绍如何在 Django 中利用中间件实现 IP 访问频率限制。通过自定义 Model 记录主机 IP、请求次数及状态,并在 Md2 中间件中处理逻辑,实现每分钟内最多 4 次请求的限制。重点解决了 Django 默认时区导致的 datetime 比较错误,以及 favicon.ico 请求干扰统计的问题,提供完整的配置与代码示例。
标题已明确表明实现方式:通过 Django 中间件来限制 IP 访问。虽然存在其他实现手段,但本文聚焦于中间件方案。
数据模型设计
前端浏览器发出的请求需经过中间件层才能到达路由和视图函数。因此,我们在中间件中增加处理逻辑,记录发起请求的 IP 地址、时间戳及请求次数,并将这些数据持久化存储以便后续校验。
首先定义用于存储限制信息的数据模型。
class Host_info(models.Model):
host = models.CharField(max_length=32)
count = models.IntegerField()
start_time = models.DateTimeField()
is_lock = models.CharField(max_length=32,default='2')
host 字段用于记录发起请求的主机 IP 地址。
count 字段用于累计该 IP 的请求次数。
start_time 字段记录该 IP 当前统计周期的起始时间。
is_lock 字段用于标识 IP 状态,默认值为 '2'(未锁定)。当值变为 '1' 时,表示该 IP 已被锁定,触发频率限制。
中间件核心逻辑
接下来定义自定义中间件类,重点实现 process_request 方法。该方法仅对请求进行拦截处理,具体代码逻辑如下,随后将讨论开发中遇到的典型问题。
自定义中间件文件 mymiddleware.py 的实现如下:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse
from app01 import models
import datetime
class Md1(MiddlewareMixin):
def process_request(self, request):
url = request.path
if url.startswith('/favicon.ico'):
return HttpResponse
class Md2(MiddlewareMixin):
def process_request(self, request):
now_time = datetime.datetime.now()
host = request.META.get('REMOTE_ADDR')
ret = models.Host_info.objects.filter(host=host).first()
if ret:
aa = now_time - ret.start_time
if aa.seconds >= 60:
ret.count = 1
ret.start_time = now_time
ret.is_lock = '2'
ret.save()
return None
if aa.seconds < 60 and ret.is_lock == '1':
return HttpResponse('登陆次数频繁,一分钟后再试')
if ret.count < 4 and ret.is_lock == '2':
if ret.count == 2:
ret.is_lock = '1'
ret.count = 0
ret.save()
else:
ret.count += 1
ret.start_time = now_time
ret.save()
return None
else:
models.Host_info.objects.create(host=host, start_time=now_time, count=1)
return None
在 Django 项目的 settings.py 文件中注册中间件。
在 MIDDLEWARE 列表中添加以下两行代码以启用中间件:
'mymiddleware.Md1',
'mymiddleware.Md2',
同时需在 settings.py 中配置时区相关参数,具体原因及配置如下文所述。
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False
常见问题与解决方案
在实现过程中主要遇到两个典型问题:
问题一涉及 datetime 对象的时区差异。由于数据表中存储的是带有时区信息的 datetime 对象,而使用 datetime.datetime.now() 获取的是本地时间(无时区信息),两者直接相减会引发类型错误或比较异常。
经排查发现,Django 默认配置 TIME_ZONE = 'UTC' 且 USE_TZ = True,导致数据库读取的时间带有 UTC 时区标记。
解决方案:在 settings.py 中将配置修改为 TIME_ZONE = 'Asia/Shanghai' 并设置 USE_TZ = False。这样保存和读取的时间均为本地时间,可直接进行数学运算。
在 Django 项目中涉及时间处理时,务必注意时区配置的一致性。
问题二涉及前端自动发起的额外请求干扰统计逻辑。
首先分析请求行为:
用户访问主页时,浏览器除了发送页面请求外,通常还会自动发起一个 favicon.ico 请求以获取网站图标。若不对此类请求做区分,会错误计入访问次数。
尽管用户只触发了一次页面加载,但浏览器响应过程中可能包含多个子请求。因此,必须排除非核心业务请求的干扰。
解决方案:在频率限制中间件之前,添加一个过滤中间件(如 Md1),专门拦截 favicon.ico 等非必要请求,确保只有有效业务请求进入限制逻辑。
## 代码其实很简单,主要是逻辑处理,你是怎么想就用代码去实现。
## 对了,这里的数据存储,你可以定义一个变量去存放存这些信息(也就是我数据表存放的这个)
## 这里唯一值得注意的就是时间了,你要很清楚知道时区这个问题。
时间差计算技巧
补充说明 datetime 对象的一个常用用法。
示例中通过两个 datetime 对象相减获取时间差,关键代码如下:
aa = now_time - ret.start_time
aa.seconds # 取到相差多少秒
上述代码中 aa 为 datetime.timedelta 类型,通过 .seconds 属性可获取相差的秒数。
以上即为基于 Django 中间件实现 IP 访问频率限制的完整流程与注意事项,希望能为相关开发工作提供参考。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













