Django 基于中间件实现 IP 访问频率限制详解

Chloe
Chloe
发布时间:2026-10-13 | 5人浏览过
原创

本文详细介绍如何在 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 访问频率限制的完整流程与注意事项,希望能为相关开发工作提供参考。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表