要实现IP访问限制,有很多种方法。根据不同的使用场景大致可以分为权限限制和爬虫限制。顾名思义权限限制就是根据权限设定特定的IP能够访问,爬虫限制是为了应对爬虫短时间内大量的访问而进行的限制。
权限限制
权限限制的实现方式可分为三种,分别是linux防火墙实现、nginx配置实现和代码中实现。
linux防火墙实现:
#阻止所有IP访问
iptables -A INPUT -s 0.0.0.0/0 -p tcp --dport 80 -j DROP
#然后再添加白名单
iptables -A INPUT -s 1.2.3.4 -p tcp --dport 80 -j ACCEPT
###############或者###########
iptables -A INPUT -s 2.3.4.5 -p tcp -j ACCEPT
nginx配置实现
##对应的location添加指定规则
location / {
allow 132.23.22.185;
deny all;
}
代码实现:
略
爬虫限制
爬虫的限制基本上都是对ip的访问频率进行限制,一般都是在代码中实现,以python为例,django进行开发时一般都会自己写一个中间件,对前端发过来的请求进行判断,下面举例说明:
方式一:REST_FRAMEWORK配置(对使用rest_framework框架的项目来说,可以使用框架对ip的访问频率进行限制)
# 仅包含相关配置部分代码
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
# 开启匿名用户接口请求频率限制
'rest_framework.throttling.AnonRateThrottle',
# 开启授权用户接口请求频率限制
'rest_framework.throttling.UserRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
# 频率限制有second, minute, hour, day
# 匿名用户请求频率
'anon': '30/second',
# 授权用户请求频率
'user': '30/second'
}
}
# views.py
class IndexView(APIView):
throttle_class = (AnonRateThrottle, UserRateThrottle)
...
方式二:利用Django的中间件来进行限制
1、使用session
'''
将ip访问的每次时间添加到session里面,在session里面维护一个长度为2的时间队列,判断当前访问的时间和第一访问的时间差
'''
import time
from django.utils.deprecation import MiddlewareMixin
MAX_REQUEST_PER_SECOND=2 #每秒访问次数
class RequestBlockingMiddleware(MiddlewareMixin):
def process_request(self,request):
now=time.time()
request_queue = request.session.get('request_queue',[])
if len(request_queue) < MAX_REQUEST_PER_SECOND:
request_queue.append(now)
request.session['request_queue']=request_queue
else:
time0=request_queue[0]
if (now-time0)<1:
time.sleep(5)
request_queue.append(time.time())
request.session['request_queue']=request_queue[1:]
#启用RequestBlocking中间件
IDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'common.middleware.RequestBlockingMiddleware', #在sessions之后,auth之前
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
2、使用表单
'''浏览器前端传来的请求,必须通过中间件,才能到后面路由,视图函数,所以我们在中间件那里做一层处理. 我们还需要知道是哪个ip,在什么时候,请求了几次,这些数据是要知道,并且记录下来,所以我创建了一个表,来存放这些信息数据'''
models文件:
class Host_info(models.Model):
host = models.CharField(max_length=32) #记录主机ip
count = models.IntegerField() #记录请求的次数
start_time = models.DateTimeField() #记录请求的时间
is_lock = models.CharField(max_length=32,default='2')#记录该ip的状态,默认为2 2代表未锁定,1代表锁定
'''接下来就是自定义中间件了,并写process_request方法,我们只对请求做处理,先贴代码,最后写遇到的一些问题. mymiddleware文件(我自定义的中间件):'''
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
#settings文件:
#添加两行代码在MIDDLEWARE列表中:
'mymiddleware.Md1',
'mymiddleware.Md2',
#并配置下面两句,原因后面会说
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False
'''遇到两个问题:
问题一:就是datetime,也就是时间分区问题,因为我数据表中需要保存到该ip访问的时间,存的时候存的是datetime对象 ,但是我从数据库中取出来这个时间,进行比较会报出错误,错误类型忘记了,我就打印了从数据库中取出的时间数据, 发现,这个时间带着时区,而我datetime.datetime.now()的时间是本机时间,根本不能相减,相比较。网上收索才 知道django默认是有时间分区的,TIME_ZONE = 'UTC',USE_TZ = True,这两句。
解决方式:在setting文件中将上面那两句修改为TIME_ZONE = 'Asia/Shanghai',USE_TZ = False。这样就解决了。在django中但凡出现时间的话,这个地方需要注意下。
问题二:额额这个问题,我在写的时候出现过,但是今天测试没那个问题,反正写上吧。我之前的错误就是我发出一个请求,首先第一个请求就是访问到url,接着第二个请求就是发出favicon.ico这种类似的,请求ico这个。以这个情况来说问题吧,你虽然在浏览器只发出一个请求,但是响应过来的网页,里面可以还有其他请求,所以这中情况需要考虑到。
解决方式:我在对用户ip做限制之前,加一个中间件,过滤掉其它的请求。,也就是上面的MD1。
补充一点,datetime的一个用法
例子中我用到datetime对象之间相减,取差多少秒,也就是这句
aa = now_time - ret.start_time
aa.seconds # 取到相差多少秒
这里的aa是datetime.timedelta类型'''
3、使用redis
def iplimit_redis(request):
ip_addr = request.META.get("REMOTE_ADDR")
try:
redis_conn = get_redis_connection("default")
ip_obj = redis_conn.get("ip_%s" % ip_addr)
if ip_obj:
data = {
"code": "li-001",
"message": "10秒之内只能请求一次"
}
return JsonResponse(data)
else:
try:
redis_conn.setex("ip_%s" % ip_addr, 10, ip_addr)
data = {
"code": "li-000",
"message": "新建用户成功",
"ip": ip_addr,
}
except:
data = {
"code": "li-002",
"message": "新建异常"
}
return JsonResponse(data)
except:
data = {
"code": "li-002",
"message": "查询id异常"
}
return JsonResponse(data)