Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。
我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。
定义一个中间件工厂函数,然后返回一个可以被调用的中间件。(闭包 知识)
中间件工厂函数需要接收一个可以调用的get_response对象。
返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象。
def simple_middleware(get_response):
# 此处编写的代码仅在Django第一次配置和初始化的时候执行一次。
def middleware(request):
# 此处编写的代码会在每个请求处理视图前被调用。
response = get_response(request)
# 此处编写的代码会在每个请求处理视图之后被调用。
return response
return middleware
例如,在users应用中新建一个middleware.py文件,
def my_middleware(get_response):
print('init 被调用')
def middleware(request):
print('before request 被调用')
response = get_response(request)
print('after response 被调用')
return response
return middleware
定义好中间件后,需要在settings.py 文件中添加注册中间件
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',
'users.middleware.my_middleware', # 添加中间件
]
定义一个视图进行测试
def demo_view(request):
print('view 视图被调用')
return HttpResponse('OK')
记得配置路由
在请求视图被处理前,中间件由上至下依次执行
在请求视图被处理后,中间件由下至上依次执行
定义两个中间件
def my_middleware1(get_response):
print('init1 被调用')
def middleware(request):
print('before request 1 被调用')
response = get_response(request)
print('after response 1 被调用')
return response
return middleware
def my_middleware2(get_response):
print('init2 被调用')
def middleware(request):
print('before request 2 被调用')
response = get_response(request)
print('after response 2 被调用')
return response
return 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',
'users.middleware.my_middleware', # 添加
'users.middleware.my_middleware2', # 添加
]
在前面课程中使用Post请求的时候,出现403错误,当时处理方式是注释掉中间件的csrf这行。这节课我们将详细讲 解什么是csrf,csrf的功能以及作用。
CSRF, Cross Site Request Forgery, 跨站点伪造请求。攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来 说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的 账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。造成个人信息泄露,与财产安全。
1.用户打开浏览器,访问A网站,输入用户名和密码请求登录网站A
2.在用户信息通过验证后,网站A产生Cookie 信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3.用户未退出网站A之前,在同一浏 览器中,打开另外一个网站B;
4.网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5.浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向
网站A发出请 求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以用户的权
限处理该请求,导致来自 网站B的恶意代码被执行。
目前防御 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求地址中添加 token 并 验证;在 HTTP 头中自定义属性并验证。
Django 防御csrf攻击: Django提供了csrf中间件用于防止CSRF攻击,而且是默认开启的。
MIDDLEWARE_CLASSES 中的csrf中间件,csrf中间件只对post请求有效,get请求无效。
1、django 第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,把这个 token 放在 cookie 里。然后每次 POST 请求都会带上这个 token
2、在返回的 HTTP 响应的 cookie 里,django 会为你添加一个 csrftoken 字段,其值为一个自动生成的 token
3、在所有的 POST 表单时,必须包含一个 csrfmiddlewaretoken 字段,只需要在表单中添加一个{%csrf_token %}标签,django会自动在模板中生成一个隐藏的表单域,带有后端响应页面时塞进来的随机生成的token值。
4、在处理 POST 请求之前,django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于 别人的 csrf 攻击,返回 403 Forbidden.
5、在所有 ajax POST 请求里,添加一个 X-CSRFTOKEN header,其值为 cookie 里的 csrftoken 的值开启了csrf中间件之后,在模板中的form表单添加一个{% csrf_token %}。 如果是用form表单提交的话直接在表单 中添加 {% csrf_token %}。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册表单</title>
</head>
<body>
{#请求方式:post请求 请求路径:/viewdemo/register/ #}
<form method="post" action="/users/login/">
{% csrf_token %}
用户名:<input type="text" name="uname"/><br>
密码:<input type="password" name="upwd"/><br>
<input type="submit" value="登录"/>
</form>
</body>
</html>
如果是用ajax提交的话,需要携带一个键为 csrfmiddlewaretoken 值为 {% csrf_token %} 的参数
示例:
如果在模板中写的 post 提交的话 可以在参数中携带这个参数: csrfmiddlewaretoken: '{{csrf_token }}
$.post('/blog/register_user/', {'uname': username,
'upwd':upwd,csrfmiddlewaretoken:
'{{ csrf_token }}'}, function (data) {
//注册成功 加载成功页面,或者重定向到其他页面
//window.href = 'http://www.baidu.com'
$('html').html(data)
}
在js文件中,可以先获取前端页面中由{% csrf_token %} 生成的隐藏域的input 标签的value值
<input name="csrfmiddlewaretoken"
value="lt5VtILNu1q44aVfQEFP2NgiUEtere0bZNnoNVSNjjX1AsfRDE9bvSn7ZmbeYL7Z"
type="hidden">
var csrf = $("input[name='csrfmiddlewaretoken']").val();
$.post('/blog/check_name/', {'uname': username,csrfmiddlewaretoken: csrf},
function (data) {
}