Django
中间件会对全部的资源请求,全部的返回方式,全部的路由到视图的跳转、全部视图层的异常进行处理。html
在Django
中,自带的有7个中间件,都具备不一样的功能。前端
目前而言了解下面这两个便可。ajax
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', # 插入session至数据表 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', # 防止跨域请求 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
这里的每个中间件其实都是一个模块,利用importlib
模块使之可以做为字符串进行导入。数据库
而且,在这些自带的中间件中,都继承了MiddlewareMixin
类。django
在该类中,提供了五个钩子方法,可以让咱们对自定义中间件进行扩展。flask
中间件钩子函数 | 描述 |
---|---|
process_request | 全部请求来时都会运行的方法 |
process_view | 全部路由匹配成功以后,跳转执行视图函数以前都会运行该方法 |
process_exception | 全部视图中有异常发生时运行的方法 |
process_response | 全部返回页面响应时运行的方法 |
process_template_response | 返回的HttpResponse对象具备render属性时才会触发该方法 |
在Django
中,请求来时中间件的执行流程是自上而下,而进行响应时中间件的执行流程都是自下而上。后端
每一个自带中间件中的钩子方法都会依次运行跨域
无论你自定义多少中间件,永远都是这个流程。浏览器
不过须要注意的是,若是你自定义了一个中间件,而且对其中的process_requset
方法进行返回了HttpResponse
,那么会同级进行返回。不一样于flask
,flask
则仍是会至下而上进行返回。缓存
自定义中间件作下面三步便可:
1.任意目录下新建一个任意名称的
.py
文件夹2.在该文件下书写任意名称的类,可是必定要继承
MiddlewareMixin
,在该类下能够进行上面五种方法的覆写3.在
settings.py
中间件进行添加路径.类名
from django.utils.deprecation import MiddlewareMixin
如我在项目全局文件夹下新建了一个文件夹。叫CustomMiddleware
,而且在里面新建了一个py
文件customMid
。
在该文件下,新建了一个类Mid
。
那么我在注册的时候就直接添加上这个路径便可:
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', 'p1.CustomMiddleware.customMid.Mid', # 新增的自定义中间件 ]
process_request
有一个参数,就是request
,这个request
和视图函数中的request
是同样的(在交给Django
后面的路由以前,对这个request
对象能够进行一系列的操做)。
返回值:默认为
None
,若是返回一个HttpResponse
对象,则将直接进行向上返回。若是是
HttpResponse
对象,Django
将不执行视图函数,而将相应对象返回给浏览器。
class Mid(MiddlewareMixin): def process_request(self, request): print("process_request")
该方法有两个参数。
多个中间件中的process_response
方法是按照MIDDLEWARE
中的注册顺序倒序执行的,也就是说第一个中间件的process_request
方法首先执行,而它的process_response
方法最后执行,最后一个中间件的process_request
方法最后一个执行,它的process_response
方法是最早执行。
class Mid(MiddlewareMixin): def process_response(self, request, response): print("process_response")
该方法有四个参数,Django
会在调用视图函数以前调用process_view
方法。
request
是HttpRequest
对象。
view_func
是Django
即将使用的视图函数。 (它是实际的函数对象,而不是函数的名称做为字符串。)
view_args
是将传递给视图的位置参数的列表.
view_kwargs
是将传递给视图的关键字参数的字典。view_args
和view_kwargs
都不包含第一个视图参数(request
)。它应该返回
None
或一个HttpResponse
对象。若是返回
None
,Django
将继续处理这个请求,执行任何其余中间件的process_view
方法,而后在执行相应的视图。若是它返回一个
HttpResponse
对象,那么将不会执行Django
的视图函数,而是直接在中间件中掉头,倒叙执行一个个process_response
方法,最后返回给浏览器
class Mid(MiddlewareMixin): def process_view(self, request, view_func, view_args, view_kwargs): print("process_view")
该方法两个参数,这个方法只有在视图函数中出现异常了才执行。
一个
HttpRequest
对象一个
exception
是视图函数异常产生的Exception
对象。它返回的值能够是一个
None
也能够是一个HttpResponse
对象。若是是
HttpResponse
对象,Django
将调用模板和中间件中的process_response
方法,并返回给浏览器,不然将默认处理异常。若是返回一个
None
,则交给下一个中间件的process_exception
方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
class Mid(MiddlewareMixin): def process_exception(self, request, exception): print("process_exception")
它有两个参数,因为执行条件很苛刻,因此用的很是少。
一个
HttpRequest
对象,一个response
对象。而且这个
response
是TemplateResponse
对象(由视图函数或者中间件产生)。
process_template_response
是在视图函数执行完成后当即执行,可是它有一个前提条件,那就是视图函数返回的对象有一个render()
方法(或者代表该对象是一个TemplateResponse
对象或等价方法)。
def index(request): # 必须有render属性/方法,该中间件钩子方法才会执行 def render(): return HttpResponse("OK") rep = HttpResponse("OK") rep.render = render return rep
class Mid(MiddlewareMixin): def process_template_response(self, request, response): # 换而言之,response必须能点出render才行 print("process_template_response") return response
因为process_exception
以及process_template_response
的触发是有条件限制的,故此再也不举例,记住他们的执行顺序是倒序便可。
因为全部的request
请求都会走这个,因此咱们能够对其进行session
控制。
维护一个集合(也能够作一个非关系型数据库,放缓存中),放上不须要session
认证的url
,称之为白名单。
若是用户未进行登陆就去访问不在白名单中的路径,则返回一个页面提示用户进行登陆。
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import redirect class Mid(MiddlewareMixin): def process_request(self,request): whitelist = {"/","/admin/","/index/","/login/","/register/"} # 不须要登陆就能访问的页面 target_url = request.path for url in whitelist: if target_url in url: return if not request.session.get("login"): # 若是未有session,表明未登陆,跳转到登陆页面 return redirect("/login/?next={0}".format(target_url))
def login(request): """ 登陆页面 """ target_url = request.GET.get("next",None) if request.method == "POST": username = request.POST.get("username") password = request.POST.get("password") if username == "Yunya" and password == "123456": request.session["login"] = True request.session.set_expiry(3600) if not target_url: return redirect("/index/") # 若是是直接点的登陆页面,登录完成后跳转到主页 else: return redirect(target_url) # 不然跳转到从其余页面过来的 return render(request,"login.html",locals())
某些IP
访问服务器的频率太高,进行拦截,好比限制每分钟不能超过10次。
若是要配合上面的白名单进行使用,这个应该注册在白名单上面。
import time from django.shortcuts import redirect from django.shortcuts import HttpResponse from django.utils.deprecation import MiddlewareMixin class Mid2(MiddlewareMixin): # 访问IP池 visit_ip_pool = {} def process_request(self, request): # 获取访问者IP ip = request.META.get("REMOTE_ADDR") # 获取访问当前时间 visit_time = time.time() # 判断若是访问IP不在池中,就将访问的ip时间插入到对应ip的key值列表,如{"127.0.0.1":[时间1]} if ip not in Mid2.visit_ip_pool: Mid2.visit_ip_pool[ip] = [visit_time] return # 而后在从池中取出时间列表 history_time = Mid2.visit_ip_pool.get(ip) # 循环判断当前ip的时间列表,有值,而且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s之内的访问时间, while history_time and visit_time-history_time[-1] > 60: history_time.pop() # 若是访问次数小于10次就将访问的ip时间插入到对应ip的key值列表的第一位置,如{"127.0.0.1":[时间2,时间1]} print(history_time) if len(history_time) < 10: history_time.insert(0, visit_time) return None else: # 若是大于10次就禁止访问 return HttpResponse("访问过于频繁,还需等待%s秒才能继续访问" % int(60-(visit_time-history_time[-1])))
跨域伪造请求我举一个例子:
有一个钓鱼网站,和银行的转帐页面如出一辙。
可是惟一不一样的地方在于,你在钓鱼网站上输好信息后点击提交,它并不会将对方卡号进行提交,而是将骗子卡号进行提交(隐藏的input
框)。这个时候银行后端收到这一条信息,你的钱就转到骗子哪儿去了。
如何解决这个问题?可使用CSRF
来防止跨域伪造请求。
在Django
中,有一个中间件就是干这个事儿的,派发随机字符串,验证随机字符串。
'django.middleware.csrf.CsrfViewMiddleware',
咱们打开它,而且在页面中添加上{% csrf_token %}
来获取到这一随机字符串,在页面上就会显示出来。
<form action="" method="POST"> {% csrf_token %} <p><input type="text" placeholder="username" name="username"></p> <p><input type="text" placeholder="password" name="password"></p> <p><button type="submit">登陆</button></p> </form>
注意!这个标签会生成一个input
框,必定要将他放在form
表单中。
而且!每次刷新页面都会生成不一样的字符串。
<input type="hidden" name="csrfmiddlewaretoken" value="yoW7bYRlhbHDcDI2KugGgHpNvjFsvZj47PNKGGXHbth2pCfITEul8NkJzN4xoUXI">
那么加上这个随机字符串后,就能够提交POST
请求了。
Ajax
提交的话,该怎么作?
如下有三种办法。
方式一
经过获取隐藏的<input>
标签中的csrfmiddlewaretoken
值,放置在data
中发送。
$.ajax({ url: "http://127.0.0.1:8000", type: "POST", data: { "username": "Yunya", "password": 123456, "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val() // 使用JQuery取出csrfmiddlewaretoken的值,拼接到data中 }, success: function (data) { console.log(data); } })
方式二
请求的键永远都是csrfmiddlewaretoken
,咱们只要把value
输入为正确的随机字符串便可。
$.ajax({ url: "/http://127.0.0.1:8000/", type: "POST", data: {"username": "Q1mi", "password": 123456,"csrfmiddlewaretoken":"{{csrf_token}}"}, success: function (data) { console.log(data); } })
方式三
经过静态文件,为全部ajax
发送请求时自动添加上csrftoken
及其随机字符串。
function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } });
前端使用时记得导入这个静态文件:
{% load static %} <script src={% static 'js/csrf.js' %}> $.ajax({ url: "/http://127.0.0.1:8000/", type: "POST", headers: {"X-CSRFToken": $.cookie('csrftoken')}, // 从Cookie取csrf_token,并设置ajax请求头 data: {"username": "Q1mi", "password": 123456}, success: function (data) { console.log(data); } })
咱们能够在视图中,为某个函数单独设置须要csrf
校验,或者取消单独某个函数的csrf
校验。
须要导入如下两个模块。
from django.views.decorators.csrf import csrf_protect # 单独校验 from django.views.decorators.csrf import csrf_exempt # 取消校验
特别注意!若是你是使用CBV
,那么取消验证时只能这样设置csrf_exempt
:
@method_decorat(csrf_exempt,name="dispatch") class Test(View): def get(self,request): pass def post(self,request): pass
关于如何为CBV
添加装饰器,你须要导入如下两个模块。
from django.views import View # 使用CBV的模块,必须继承该类 from django.utils.decorators import method_decorator # 添加装饰器的模块