Django中间件(中间件版登陆验证、访问频率限制)
一、介绍
官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。 但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。 说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。 我们一直都在使用中间件,只是没有注意到而已,打开Django项目的Settings.py文件,看到下图的MIDDLEWARE配置项。 MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
] 二、中间件的使用
1、 作用
全局改变Django的请求和响应 2、 Django中自定义中间件
1.在项目下新建一个python page包(例如:mymiddleware),包内新建py文件(例如:my_middleware)
在py文件内定义中间件
示例:
from django.utils.deprecation import MiddlewareMixin class MD1(MiddlewareMixin): def process_request(self, request):
print("MD1里面的 process_request") def process_response(self, request, response):
print("MD1里面的 process_response")
return response 然后去settings.py里面注册MIDDLEWARE
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'mymiddleware.my_midware.MD1',
]
注册中间件
2.五个方法(执行的时间点、执行的顺序、参数、返回值)
1. process_request(self,request) *****
0. 执行的时间点:
请求进来之后,执行view视图函数前
1. 执行顺序:
按照中间件的注册顺序执行
2. 参数
request:当前请求对象(从哪个url进来的,request在中间件和views函数中都是那个url的request)
3. 返回值
1. 返回None继续执行后续的
2. 返回响应对象,不继续执行后续的流程,直接返回响应 2. process_response(self, request, response) *****
0. 执行的时间点:
返回响应之后(即经过了views.py里面的函数之后)
1. 执行顺序:
按照中间件注册的倒序执行
2. 参数
1. request:当前的请求对象
2. response: 从views.py对应的函数中传递过来的响应对象
3. 返回值:
1. 必须返回一个响应对象 3. process_view(self, request, view_func, view_args, view_kwargs)
0. 执行的时间点:
process_request之后,视图函数之前执行
1. 执行顺序
按照注册的顺序执行
2. 参数
1. request:请求对象
2. view_func: 将要执行的视图函数对象
3. view_args/view_kwargs: 将要执行的函数的参数
3. 返回值
1. None:继续往后执行
2. 响应对象: 直接返回了,不会执行后续的视图函数 4. process_template_response(self,request,response)
0. 执行的时间点
当视图函数中返回带render方法的响应对象,这个方法才会执行
1. 执行顺序:
注册的倒序
2. 参数:
1. request:请求对象
2. response:响应对象
3. 返回值
1. 必须返回响应对象 5. process_exception(self, request, exception)
0. 执行的时间点
当视图函数中抛出异常的时候才会执行这个方法
1. 执行顺序:
注册的倒序
2. 参数:
1. request:请求对象
2. exception: 视图函数抛出的异常
3. 返回值:
1. None:继续执行后续
2. 响应对象: 直接返回
6.小结
1.中间件的执行顺序:
只要还没到视图函数,中间件的执行顺序就是 注册的顺序
只要到了视图函数,或者已经执行过了视图函数,中间件的执行顺序就是 注册的倒序 3、例子
1.process_request(self, request)
class M1(MiddlewareMixin):
def process_request(self, request):
print(request, id(request))
print('in M1 process_request')
# return HttpResponse('ok') class M2(MiddlewareMixin):
def process_request(self, request):
print(request, id(request))
print('in M2 process_request')
2.process_response(self, request, response)
class M1(MiddlewareMixin):
def process_request(self, request):
print(request, id(request))
print('in M1 process_request')
# return HttpResponse('ok') def process_response(self, request, response):
print('in M1 process_response')
return response # 必须返回一个对象 class M2(MiddlewareMixin):
def process_request(self, request):
print(request, id(request))
print('in M2 process_request') def process_response(self, request, response):
print('in M2 process_response')
return response # 必须返回一个对象
3.process_view(self, request, view_func, view_args, view_kwargs)
class M1(MiddlewareMixin):
def process_request(self, request):
print(request, id(request))
print('in M1 process_request')
# return HttpResponse('ok') def process_response(self, request, response):
print('in M1 process_response')
return response def process_view(self, request, view_func, view_args, view_kwargs):
print(view_func)
# view_func(request) # 在中间件中手动调用将要执行的视图函数
print('这是M1里面的process_view方法')
# return HttpResponse('OJ8K') class M2(MiddlewareMixin):
def process_request(self, request):
print(request, id(request))
print('in M2 process_request') def process_response(self, request, response):
print('in M2 process_response')
return response def process_view(self, request, view_func, view_args, view_kwargs):
print(view_func)
print('这是M2里面的process_view方法')
4.process_template_response(self,request,response)
views.py
def test(request):
print(request, id(request))
print('-------------in test--------------------------------------')
rep = HttpResponse('我是原本的响应')
def render():
return HttpResponse('我有render方法啦,记住是方法!')
rep.render = render # 给响应对象设置一个属性render,给这个属性添加render方法
return rep
中间件
class M1(MiddlewareMixin):
def process_request(self, request):
print(request, id(request))
print('in M1 process_request')
# return HttpResponse('ok') def process_response(self, request, response):
print('in M1 process_response')
return response def process_view(self, request, view_func, view_args, view_kwargs):
print(view_func)
print('这是M1里面的process_view方法')
# return HttpResponse('OJ8K') def process_template_response(self, request, response):
print('这是M1中process_template_response方法')
return(response) # 必须要有响应对象 class M2(MiddlewareMixin):
def process_request(self, request):
print(request, id(request))
print('in M2 process_request') def process_response(self, request, response):
print('in M2 process_response')
return response def process_view(self, request, view_func, view_args, view_kwargs):
print(view_func)
print('这是M2里面的process_view方法')
5.process_exception(self, request, exception)
views.py
def test(request):
print(request, id(request))
print('-------------in test--------------------------------------')
raise ValueError('英雄联盟')
return HttpResponse('我是test视图函数')
中间件
class M1(MiddlewareMixin):
def process_request(self, request):
print(request, id(request))
print('in M1 process_request')
# return HttpResponse('ok') def process_response(self, request, response):
print('in M1 process_response')
return response def process_view(self, request, view_func, view_args, view_kwargs):
print(view_func)
print('这是M1里面的process_view方法')
# return HttpResponse('OJ8K') def process_template_response(self, request, response):
print('这是M1中process_template_response方法')
return(response) def process_exception(self, request, exception):
print('这是M1中的process_exception')
print(exception)
return HttpResponse('视图函数报错啦!') class M2(MiddlewareMixin):
def process_request(self, request):
print(request, id(request))
print('in M2 process_request') def process_response(self, request, response):
print('in M2 process_response')
return response def process_view(self, request, view_func, view_args, view_kwargs):
print(view_func)
print('这是M2里面的process_view方法') def process_exception(self, request, exception):
print('这是M2中的process_exception')
三、中间件的流程图
1、django框架完整流程图
2、process_request和process_response
当process_request没有返回响应对象的时候,会继续执行后面的操作,顺利执行完后,会执行视图函数,最后执行process_response,
当process_request返回了响应对象,那么它会立刻执行自己的process_response方法,
此时process_response中的response参数接收的是process_request返回的响应对象
3、process_request和process_response和process_view
当process_request没有返回响应对象的时候,会继续执行后面的process_request操作,
当process_request返回了响应对象,那么它会立刻执行自己的process_response方法,
此时process_response中的response参数接收的是process_request返回的响应对象,
当process_request没有返回响应对象,顺利执行完后,将会执行process_view,
当process_view没有返回响应对象的时候,会继续执行后面的process_view操作,顺利执行完后,会执行视图函数,最后执行process_response,
当process_view返回了一个响应对象的时候,后续操作(除了process_response)将不会执行,
而是把响应对象返回给最后一个process_response方法。
4、总结
请求进来的时候,按顺序执行process_request,然后执行process_view,执行完后,再执行views.py里面的视图函数,最后按倒叙执行process_response,
process_response无论如何都是会执行的,它是做响应处理,然后把响应返回给浏览器的函数,
当process_request返回了一个响应对象的时候,后续操作(除了process_response)将不会执行,而是直接把响应对象返回给自己的process_response方法,
当process_view返回了一个响应对象的时候,后续操作(除了process_response)将不会执行,而是把响应对象返回给最后一个process_response方法。
注意:
一般情况下是process_response做响应处理。 当视图函数中返回带render方法的响应对象时,执行process_template_response做响应处理,然后process_template_response会把响应再返回给process_response做最后的响应处理。 当视图函数中抛出异常的时候,执行process_exception做响应处理,然后process_exception会把响应再返回给process_response做最后的响应处理。
5、拓展
注册中间件的时候,我们写入的是字符串,那么django是如何找到我定义的具体的中间件函数呢?
实际上,使用的是importlib模块和反射
例如:我注册是中间件是 'mymiddleware.my_midware.CheckLogin'
import importlib
# importlib类似于反射,importlib可以一直反射到模块(py文件)
# 反射只能单一反射某个模块内的变量
# 因此importlib和反射配合使用可以达到把字符串反射成我们需要的变量
s = 'mymiddleware.my_midware.CheckLogin' # 具体到模块的变量,importlib不能反射,需要分割一下
s1 = s.rsplit('.', 1) # ['mymiddleware.my_midware', 'CheckLogin']
module_obj = importlib.import_module(s1[0]) # 把字符串'mymiddleware.my_midware'反射出来
if hasattr(module_obj, s1[1]):
class_obj = getattr(module_obj, s1[1]) # 把'CheckLogin'反射出来
print(class_obj)
四、示例
1、中间件版登录验证
1.views.py
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
pwd = request.POST.get('password')
is_rem = request.POST.get('remember', None)
# 不推荐使用get,因为get取不到值会报错
user_obj = UserInfo.objects.filter(name=username, password=pwd).first()
if user_obj:
return_url = request.GET.get('returnUrl', '/index/')
# 设置session的键值对
request.session['user'] = user_obj.name
# 判断是否记住密码
if is_rem:
# 是就保存七天
request.session.set_expiry(60 * 60 * 24 * 7)
else:
# 不是就不保存
request.session.set_expiry(0)
return redirect(return_url)
else:
return render(request, 'login.html', {'error_msg': '用户名或者密码错误'}) return render(request, 'login.html') def index(request):
return render(request, 'index.html') def home(request):
return render(request, 'home.html') def logout(request):
request.session.flush()
return redirect('/login/')
2.settings.py配置白名单
# 白名单可写在中间件那里,也可以写在settings那里,为了方便后续的修改,一般写在setting
# 白名单:不需要登录即可访问的URL
WHITE_URLS = ['/login/']
3.中间件
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect, render, HttpResponse
from django.conf import settings
from myapp.models import * # 自定义登录验证的中间件
class CheckLogin(MiddlewareMixin): def process_request(self, request):
# 判断请求的url是否在白名单
while_urls = settings.WHITE_URLS if hasattr(settings, 'WHITE_URLS') else []
if request.path_info in while_urls:
return None
# 查看请求的数据中是否携带我设置的登录状态
is_login = request.session.get('user', None)
if not is_login:
# 没有登录,跳转到登录界面
# 获取当前访问的url
next_url = request.path_info
return redirect('/login/?returnUrl={}'.format(next_url))
else:
# 已经登录了,获取登录的对象
user_obj = UserInfo.objects.get(name=is_login)
# 把登录对象赋值给request的use属性(自定义属性)
request.user = user_obj
2、访问频率限制
1.settings.py设置访问频率限制
ACCESS_LIMIT = 10
2.中间件
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect, render, HttpResponse
from django.conf import settings
import time
import redis # 存放每个ip访问页面的大字典
ACCESS_RECORD = {}
# 自定义访问频率限制
class Throttle(MiddlewareMixin): def process_request(self, request):
access_limit = settings.ACCESS_LIMIT if hasattr(settings, 'ACCESS_LIMIT') else 10
# 拿到当前请求的ip地址
ip = request.META.get('REMOTE_ADDR') # 初始化字典,把每个新进来的请求存到ACCESS_RECORD大字典里面
if ip not in ACCESS_RECORD:
ACCESS_RECORD[ip] = [] # 从字典中拿到当前访问的ip的列表[列表只存距离现在10秒内的访问时间点]
ip_access_list = ACCESS_RECORD[ip] # 拿到当前时间
now = time.time() # 把距离当前时间大于10秒的访问时间点从列表中删除
while ip_access_list and now - ip_access_list[-1] > access_limit:
ip_access_list.pop() # 距离当前时间小于10秒的访问时间追加进访问列表里面
ip_access_list.insert(0, now) # 判断最近10秒中之内访问次数是否大于3
if len(ip_access_list) > 3:
return HttpResponse('滚')
3.redis版本
# -*- coding: utf-8 -*-
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect, render, HttpResponse
from django.conf import settings
import time
import redis conn = redis.Redis(host='localhost', port=6379, decode_responses=True) # 自定义访问频率限制
class RedisThrottle(MiddlewareMixin): def process_request(self, request):
# 频率
access_limit = settings.ACCESS_LIMIT if hasattr(settings, 'ACCESS_LIMIT') else 10
# 拿到当前请求的ip地址
ip = request.META.get('REMOTE_ADDR')
# 把访问的ip存到redis: ip: 访问次数
if conn.exists(ip):
conn.incr(ip)
else:
conn.set(ip, 1)
conn.expire(ip, access_limit) # 如果访问次数超过访问频率, 10秒内只能访问3次
if int(conn.get(ip)) > 3:
return HttpResponse('滚')
Django中间件(中间件版登陆验证、访问频率限制)的更多相关文章
- Django之Cookie Session详解,CBV,FBV登陆验证装饰器和自定义分页
Cookie Session和自定义分页 cookie Cookie的由来 大家都知道HTTP协议是无状态的. 无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接 ...
- Django组件(三) Django之中间件
中间件概述 中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出.因为改变的是全局,所以需要谨慎实用,用不好会影响到性 ...
- Cookie、Session登陆验证相关介绍和用法
一.Cookie和Session 首先.HTTP协议是无状态的:所谓的无状态是指每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应直接影响,也不会直接 ...
- {Django基础九之中间件} 一 前戏 二 中间件介绍 三 自定义中间件 四 中间件的执行流程 五 中间件版登陆认证
Django基础九之中间件 本节目录 一 前戏 二 中间件介绍 三 自定义中间件 四 中间件的执行流程 五 中间件版登陆认证 六 xxx 七 xxx 八 xxx 一 前戏 我们在前面的课程中已经学会了 ...
- Django中间件限制用户访问频率
原:https://blog.csdn.net/weixin_38748717/article/details/79095399 一.定义限制访问频率的中间件 common/middleware.py ...
- Django 中间件实现用户认证与IP频率限制
1.URL访问过滤 通过装饰器进行用户认证非常方便,但是在添加部分需要认证的功能时,就需要再次添加装饰器,如果通过中间件来实现,就不需要再进行添加的操作. import re LOGIN_URL = ...
- (28)django的中间件(自定义中间件和防范跨站请求伪造攻击)-重要的概念
Django中间件和中间件不是同一种东西 什么是中间件:中间件是一个很大的概念,只要程序和程序之间还有一层程序,用来处理两个程序的整个交互过程的请求.数据等等就叫中间件 Django中间件:是介于re ...
- python 全栈开发,Day87(ajax登录示例,CSRF跨站请求伪造,Django的中间件,自定义分页)
一.ajax登录示例 新建项目login_ajax 修改urls.py,增加路径 from app01 import views urlpatterns = [ path('admin/', admi ...
- Django框架----中间件
我们已经会了给视图函数加装饰器来判断是用户是否登录,把没有登录的用户请求跳转到登录页面.我们通过给几个特定视图函数加装饰器实现了这个需求.但是以后添加的视图函数可能也需要加上装饰器,这样是不是稍微有点 ...
随机推荐
- Monorepo All In One
Monorepo All In One monorepos 只是一种思想,或设计模式,架构风格 https://trunkbaseddevelopment.com/monorepos/ Lerna h ...
- UX & feedback & instant visual feedback
UX & feedback & instant visual feedback Select an element on the page https://ant.design/com ...
- git & Angular git commit 规范
git & Angular git commit 规范 https://github.com/angular/angular/commits/master https://github.com ...
- taro css 转换 bug
taro css 转换 bug https://nervjs.github.io/taro/docs/size.html https://nervjs.github.io/taro/docs/comp ...
- 在next主题添加微信公众号二维码
在侧边栏添加微信公众号二维码 首先,当然是准备一张微信公众号二维码.有两种添加方式,添加到侧边栏或者添加到推文的结尾处.我的next主题是7.x版本的,使用的主题是Gemini,设置的侧栏显示方式是一 ...
- 翻译:《实用的Python编程》02_03_Formatting
目录 | 上一节 (2.2 容器) | 下一节 (2.4 序列) 2.3 格式化 虽然本节稍微有点离题,但是当处理数据时,通常想要生成结构化的输出(如表格).示例: Name Shares Price ...
- WPF MVVM实例三
在没给大家讲解wpf mwm示例之前先给大家简单说下MVVM理论知识: WPF技术的主要特点是数据驱动UI,所以在使用WPF技术开发的过程中是以数据为核心的,WPF提供了数据绑定机制,当数据发生变化时 ...
- LDAP + Samba 安装配置流程
LDAP + Samba 安装配置 基础环境:Ubuntu18.04 安装samba root@cky:~# apt install samba smbldap-tools -y 查看版本 root@ ...
- Go语言学习之路-11-方法与接口
目录 编程方式 go语言对象方法 自定义类型和方法 接收器: 方法作用的目标(类型和方法的绑定) go面向对象总结 方法的继承 go语言接口 为什么要用接口 接口的定义 接口的作用总结 接口的嵌套 空 ...
- 后端程序员之路 59、go uiprogress
gosuri/uiprogress: A go library to render progress bars in terminal applicationshttps://github.com/g ...