CSRF(Cross-site request forgery),中文名称是跨站请求伪造。什么意思呢?简单的说,就是用户在网站A登录之后,网站生成了对应的Cookie等信息,然后这个时候,用户又打开了网站B,网站B可以在提交表单的时候,指定对象的地址为网站A,这样就对网站A提出了一个请求。
为了避免这种恶意请求,一般的方法是在客户端生成一个Token,每次提交来的请求,服务器都会验证这个Token之后才会放行。Django里面,默认配置文件里面有一个中间件,可以帮我们实现这个功能。
1
2
3
4
5
6
7
8
9
|
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'
,
]
|
在前面的例子里面,我们都是注释掉了csrf这个中间件。因为一旦使用的话,会自动在Cookie里面生成一个CSRFToken。相对应的,在模板文件里面我们也必须提供对应的csrftoken,否则提交post请求的时候,就会出现下面的错误。
在打开着CSRF的中间件之后,如果观察Cookie,会发现他自动生成了一个csrftoken的键值对。
如何使用呢?最简单的方式就是在模板文件的form里面添加一行 {% csrf_token %},这样就行了
1
2
3
4
5
6
7
8
9
|
<
form
action
=
"/login/"
method
=
"POST"
>
{% csrf_token %}
<
input
type
=
"text"
name
=
"user"
/>
<
input
type
=
"text"
name
=
"pwd"
/>
<
input
type
=
"checkbox"
name
=
"rmb"
value
=
"1"
/> 10秒免登录
<
input
type
=
"submit"
value
=
"提交"
/>
<
input
id
=
"btn1"
type
=
"button"
value
=
"按钮"
/>
<
input
id
=
"btn2"
type
=
"button"
value
=
"按钮"
/>
</
form
>
|
除了POST提交数据,我们还可以通过AJAX来提交数据。那么在AJAX里面应该如何使用呢?有两种方式:
-
可以在具体的一个AJAX请求里面添加一个header,header里面的csrftoken通过cookie获取
1
2
3
4
5
6
7
8
9
10
|
$(
'#btn1'
).click(
function
() {
$.ajax({
url:
'/login/'
,
type:
"GET"
,
data: {
'user'
:
'root'
,
'pwd'
:
'123'
},
headers: {
'X-CSRFtoken'
: $.cookie(
'csrftoken'
)},
success:
function
(arg){
}
})
});
|
2.我们还可以针对所有的AJAX请求统一配置,这样在执行AJAX发送之前 会自动执行下面的代码
1
2
3
4
5
|
$.ajaxSetup({
beforeSend:
function
(xhr,settings){
xhr.setRequestHeader(
'X-CSRFtoken'
, $.cookie(
'csrftoken'
));
}
});
|
除了通过中间件全局的配置CSRF,我们还可以进行局部的调整,允许或者不允许执行CSRF。
Django提供了两个装饰器函数 csrf_exempt 和csrf_protector。
我们如果关闭了全局的CSRF,但是又希望对个别函数进行保护,可以在对应的函数上面使用csrf_protector这个装饰器。(不推荐)
例如:
1
2
3
4
|
from
django.views.decorators.csrf
import
csrf_exempt,csrf_protect
@csrf_protect
def
index(request):
print
(request.session)
|
另外一个情况就是,我们打开了全局的CSRF,但是希望对个别函数不进行保护,那么使用csrf_exempt