#前言:
该博客主要讲解了在Django中,中间件的一些特点及其操作,最后会讲解如何对IP进行封禁和拉黑。后续我还会继续更新关于Python开发中的一些内容,欢迎大家关注。
一、简介:
中间件,是一种可以介入 django 的请求和响应的处理过程,能修改 django 的响应的数据。中间件提供了一种无侵入式的开发方式。
而中间件的主要作用是什么?
1、认证登录
2、统计访问量
3、拦截请求
如图所示,中间件就是可以在 django 处理视图的不同阶段进行干预。
而中间件的响应顺序:
1、请求进来的时候需要经过中间件之后才能进行路由匹配
2、响应出去的时候也需要经过中间件
二、Django 框架原先内置并启动了一些中间件:
在setting.py文件下:
MIDDLEWARE = [
# 主要对安全性的处理请求,将 http 请求重定向为 https 请求
'django.middleware.security.SecurityMiddleware',
# 会话支持
'django.contrib.sessions.middleware.SessionMiddleware',
# 检查是否允许浏览器访问类型 , 检查 url 是否需要添加 /
'django.middleware.common.CommonMiddleware',
# 跨站点防御保护
'django.middleware.csrf.CsrfViewMiddleware',
# 用户认证
'django.contrib.auth.middleware.AuthenticationMiddleware',
# Cookie 会话支持
'django.contrib.messages.middleware.MessageMiddleware',
# 点击劫持保护
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
在请求处理之前,中间件根据配置文件中的中间件的配置列表从上往下依次执行
在请求处理之后,中间件根据配置文件中的中间件的配置列表从下往上依次执行
三、自定义中间件:
定义一个中间件工厂函数,返回一个可以被调用的中间件(闭包)。
中间件的工厂函数必须接收一个可以被调用的 get_response 对象请求处理。
1.在应用下新建一个 middleware.py 文件:
2.编写自定义中间件:
(具体代码的作用在注释中都有)
def simple_middleware(get_response):
# 这里编写的代码 只在 Django 项目启动的时候被执行
print("初始化 Django")
def middleware(request):
'''
request: 和视图接收的 Request 数据是一致,都是浏览器的请求数据
'''
# 这里编写的代码,是在请求处理前被执行
print('request 请求被处理前')
response = get_response(request)
# 这里编写的代码,是在请求处理后执行
print('request 请求被处理后')
return response
return middleware
3.定义好中间件之后,需要到配置文件中的 MIDDLEWARE 列表中注册自定义中间件路径:
# 自定义中间件
'MiddlewareApp.middleware.simple_middleware',
4.最后在路由文件中和视图文件中响应中间件。
注意:
当有多个中间件同时响应的时候,如果有两个中间件,初始化是先输出后面的中间件再输出前面的,因为初始化是先访问中间件,但是输出的时候相当于要响应出去,响应出去就从后往前了。
四、自定义中间件类:
自定义中间件类需要继承 MiddlewareMixin
process_request 方法 , 是 Request 预处理函数;这个方法在请求处理之前被调用。
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render , HttpResponse
class MyMiddleware(MiddlewareMixin):
def process_request(self , request):
# request: 用户请求的数据
# 可以没有返回值:None 中间件就会按照正常流程执行 ,
# 如果配置列表中这个中间件后面还有中间件就进入到下一个中间中,否则就进入 url 请求匹配
print("process_request 预处理函数")
# 有返回值:HttpResponse 对象 或者其子类对象,就不执行处理其他中间件的内容,
# 以及不进行 url 的匹配,到这里整个请求结束,然后将这个定义的返回值响应到浏览器中
return HttpResponse('我是 process_request 响应的数据')
1.process_response 方法:
请求处理之后被调用
def process_response(self , request , response):
'''
:param request: 用户请求的数据
:param response: 视图返回出来的 HttpResponse 对象
:return: 必须要有返回值,必须返回一个 HttpResponse 对象
可以返回视图的 response 对象
也可以自定义的 HttpResponse 对象,则是视图中响应结果无效
'''
print('process_response 执行')
# return response
return HttpResponse('我是 process_response 响应的数据')
2.process_view 方法:
这个方法在路由匹配之后 ,视图响应之前
def process_view(self , request , view_func , view_args , view_kwargs):
'''
:param request:
:param view_func: 请求的 url 匹配到的视图函数
:param view_args: 是给视图传递的参数
:param view_kwargs: 是给视图传递的参数
:return: 可以没有返回值;可以返回匹配到的视图函数,
也可以自定义的 HttpResponse 对象,则是视图中响应结果无效
'''
print('process_view 执行')
# return view_func(request)
return HttpResponse('我是 process_view 响应的数据')
五、中间件拦截请求:
1、限制 IP 访问频率:
主题思路:
1、获取到访问主机的 IP
2、获取到 IP 访问的时间
3、时间间隔:例如每四十秒访问最多15次
visit_ip = {}
class IpMiddleware(MiddlewareMixin):
'''
限制 IP 访问频率
1、获取到访问主机的 IP
2、获取到 IP 访问的时间
3、时间间隔:40s,次数15次
'''
def process_request(self , request):
# 获取用户访问的主机 IP
ip = request.META.get('REMOTE_ADDR')
# 获取 IP 访问的时间
visit_time = time.time()
# 数据保存格式:{IP:[访问时间,……],IP2:[访问时间,……]……}
# 判断 IP 在字典中是否存在,如果存在,说明这个 IP 不是第一次访问
# 否则 添加这个 IP 的键值对数据,是第一次访问
if ip not in visit_ip:
visit_ip[ip] = [visit_time]
# IP 在字典中存在, 将访问的时间间隔大于 40s 的进行删除记录
old_time = visit_ip.get(ip)
for t in old_time:
# 如果列表中获取到的时间和当前返回的时间超过 40s 则清除掉
if visit_time - t > 40:
old_time.remove(t)
# 判断时间列表中记录是否有超过 15 次
# 没有超过 , 进行记录当前的访问时间
# 超过,限制当前 IP 访问
if len(old_time) < 15:
old_time.insert(0 , visit_time)
else:
return HttpResponse("您别累着了 , 休息一下吧!!!")
2、禁止 IP 访问:
关于禁止IP访问和上述限制IP访问就是多一层判断而已。
visit_ip = {}
black_ip = {} # {IP:count}
class IpMiddleware(MiddlewareMixin):
'''
限制 IP 访问频率
1、获取到访问主机的 IP
2、获取到 IP 访问的时间
3、时间间隔:40s,次数15次
4、保存用户不良行为记录次数 , 超出 3
'''
def process_request(self , request):
# 获取用户访问的主机 IP
ip = request.META.get('REMOTE_ADDR')
# 检查 IP 是否被拉黑
black_num = black_ip.get(ip , 0)
if black_num > 3:
# IP 被拉黑 , 不允许访问
return HttpResponse('庙小 , 容不下大佛')
# 获取 IP 访问的时间
visit_time = time.time()
# 数据保存格式:{IP:[访问时间,……],IP2:[访问时间,……]……}
# 判断 IP 在字典中是否存在,如果存在,说明这个 IP 不是第一次访问
# 否则 添加这个 IP 的键值对数据,是第一次访问
if ip not in visit_ip:
visit_ip[ip] = [visit_time]
# IP 在字典中存在, 将访问的时间间隔大于 40s 的进行删除记录
old_time = visit_ip.get(ip)
for t in old_time:
# 如果列表中获取到的时间和当前返回的时间超过 40s 则清除掉
if visit_time - t > 40:
old_time.remove(t)
# 判断时间列表中记录是否有超过 15 次
# 没有超过 , 进行记录当前的访问时间
# 超过,限制当前 IP 访问
if len(old_time) < 5:
old_time.insert(0 , visit_time)
else:
# 保存累计不良行为的次数
black_ip[ip] = black_num + 1
return HttpResponse("您别累着了 , 休息一下吧!!!")
注意:
我上述代码中用到的都是对字典数据进行判断,而对接到项目中时,可以对接到数据库来进行操作(与上述代码中思路一致),这样更加方便操作。
六、总结:
该博客主要讲解了在Django中,中间件的一些特点及其操作,及其如何对IP进行封禁和拉黑。欢迎大家前来指正。