Django---CSRF的装饰器,CSRF的流程,JSON数据格式,ajax技术(基于JQ实现)
Django---CSRF的装饰器,CSRF的流程,JSON数据格式,ajax技术(基于JQ实现)
一丶CSRF相关的装饰器
from django.utils.decorators import method_decorator # 给cbv加上装饰器
from django.views import View
from django.views.decorators.csrf import csrf_exempt, csrf_protect
### 在FBV模式下
# csrf_exempt 豁免csrf校验
# csrf_protect 强制进行csrf校验
'''
csrf_exempt: #豁免,不进行csrf校验
csrf_protect: #强制执行csrf校验
'''
@csrf_exempt #豁免,不进行csrf校验
def csrf_check(request):
return render(request,'csrf_check.html')
### 在CBV模式下
# csrf_exempt要加在CBV上,只能加dispatch上
@method_decorator(csrf_exempt,name='dispatch') # 豁免csrf 和 强制csrf ,在CBV上必须添加在dispatch方法上
class CSRF_CHECK(View):
def post(self,request):
return HttpResponse('post,ok')
def get(self,request):
return render(request,'csrf_check.html')
二丶CSRF的流程
1.想要通过csrf校验的前提是. 必须有csrftoken的cookie.
### 生成 input标签 携带csrf的值
# 1. {% csrf_token %}
<input type="hidden" name="csrfmiddlewaretoken" value="uVC0dQAf4R9cIfT57OUGFgQTiggYFUD4qwhBYPVLJSwPN2RoiMQSWGNvpFnnkmAX">
# 2. 浏览器中的session中存在csrftoken值
# csrftoken WK1xwuRTVyVLDMTfgsx8cpPDOJZPCAhZSlG8htcpAzioIzRyrqtktPMfV86eh2eS
from django.views.decorators.csrf import ensure_csrf_cookie # 确保有session的值
@method_decorator(ensure_csrf_cookie) # ensure_csrf_cookie 确保响应的数据中包含session的csrftoken值
def get(self,request):
return render(request,'csrf_check.html')
2.从cookie中获取csrftoken的值 与 POST提交的数据中的csrfmiddlwaretoken的值做比对
# 如果从request.POST中获取不到csrfmiddlewaretoken的值,会尝试从请求头中获取x-csrftoken的值,并且拿这个值与csrftoken的值做对比,对比成功也能通过校验。
CSRF源码 如下:
#### csrf的源码
from django.middleware.csrf import CsrfViewMiddleware
### 主要了解 三个方法:
# def process_request(self, request): 处理请求
# def process_view(self, request, callback, callback_args, callback_kwargs): 视图处理
# def process_response(self, request, response): 响应处理
class CsrfViewMiddleware(MiddlewareMixin):
"""
Middleware that requires a present and correct csrfmiddlewaretoken
for POST requests that have a CSRF cookie, and sets an outgoing
CSRF cookie.
This middleware should be used in conjunction with the csrf_token template
tag.
"""
# The _accept and _reject methods currently only exist for the sake of the
# requires_csrf_token decorator.
def _accept(self, request):
# Avoid checking the request twice by adding a custom attribute to
# request. This will be relevant when both decorator and middleware
# are used.
request.csrf_processing_done = True
return None
def _reject(self, request, reason):
logger.warning(
'Forbidden (%s): %s', reason, request.path,
extra={
'status_code': 403,
'request': request,
}
)
return _get_failure_view()(request, reason=reason)
def _get_token(self, request):
if settings.CSRF_USE_SESSIONS:
try:
return request.session.get(CSRF_SESSION_KEY)
except AttributeError:
raise ImproperlyConfigured(
'CSRF_USE_SESSIONS is enabled, but request.session is not '
'set. SessionMiddleware must appear before CsrfViewMiddleware '
'in MIDDLEWARE%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '')
)
else:
try:
cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
except KeyError:
return None
csrf_token = _sanitize_token(cookie_token)
if csrf_token != cookie_token:
# Cookie token needed to be replaced;
# the cookie needs to be reset.
request.csrf_cookie_needs_reset = True
return csrf_token
def _set_token(self, request, response):
if settings.CSRF_USE_SESSIONS:
request.session[CSRF_SESSION_KEY] = request.META['CSRF_COOKIE']
else:
response.set_cookie(
settings.CSRF_COOKIE_NAME,
request.META['CSRF_COOKIE'],
max_age=settings.CSRF_COOKIE_AGE,
domain=settings.CSRF_COOKIE_DOMAIN,
path=settings.CSRF_COOKIE_PATH,
secure=settings.CSRF_COOKIE_SECURE,
httponly=settings.CSRF_COOKIE_HTTPONLY,
)
# Set the Vary header since content varies with the CSRF cookie.
patch_vary_headers(response, ('Cookie',))
def process_request(self, request):
csrf_token = self._get_token(request) # 从cookie中获取csrftoken的cookie值
if csrf_token is not None:
# Use same token next time.
request.META['CSRF_COOKIE'] = csrf_token
def process_view(self, request, callback, callback_args, callback_kwargs):
if getattr(request, 'csrf_processing_done', False):
return None
# Wait until request.META["CSRF_COOKIE"] has been manipulated before
# bailing out, so that get_token still works
if getattr(callback, 'csrf_exempt', False):
return None
# Assume that anything not defined as 'safe' by RFC7231 needs protection
if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
if getattr(request, '_dont_enforce_csrf_checks', False):
# Mechanism to turn off CSRF checks for test suite.
# It comes after the creation of CSRF cookies, so that
# everything else continues to work exactly the same
# (e.g. cookies are sent, etc.), but before any
# branches that call reject().
return self._accept(request)
if request.is_secure():
# Suppose user visits http://example.com/
# An active network attacker (man-in-the-middle, MITM) sends a
# POST form that targets https://example.com/detonate-bomb/ and
# submits it via JavaScript.
#
# The attacker will need to provide a CSRF cookie and token, but
# that's no problem for a MITM and the session-independent
# secret we're using. So the MITM can circumvent the CSRF
# protection. This is true for any HTTP connection, but anyone
# using HTTPS expects better! For this reason, for
# https://example.com/ we need additional protection that treats
# http://example.com/ as completely untrusted. Under HTTPS,
# Barth et al. found that the Referer header is missing for
# same-domain requests in only about 0.2% of cases or less, so
# we can use strict Referer checking.
referer = force_text(
request.META.get('HTTP_REFERER'),
strings_only=True,
errors='replace'
)
if referer is None:
return self._reject(request, REASON_NO_REFERER)
referer = urlparse(referer)
# Make sure we have a valid URL for Referer.
if '' in (referer.scheme, referer.netloc):
return self._reject(request, REASON_MALFORMED_REFERER)
# Ensure that our Referer is also secure.
if referer.scheme != 'https':
return self._reject(request, REASON_INSECURE_REFERER)
# If there isn't a CSRF_COOKIE_DOMAIN, require an exact match
# match on host:port. If not, obey the cookie rules (or those
# for the session cookie, if CSRF_USE_SESSIONS).
good_referer = (
settings.SESSION_COOKIE_DOMAIN
if settings.CSRF_USE_SESSIONS
else settings.CSRF_COOKIE_DOMAIN
)
if good_referer is not None:
server_port = request.get_port()
if server_port not in ('443', '80'):
good_referer = '%s:%s' % (good_referer, server_port)
else:
# request.get_host() includes the port.
good_referer = request.get_host()
# Here we generate a list of all acceptable HTTP referers,
# including the current host since that has been validated
# upstream.
good_hosts = list(settings.CSRF_TRUSTED_ORIGINS)
good_hosts.append(good_referer)
if not any(is_same_domain(referer.netloc, host) for host in good_hosts):
reason = REASON_BAD_REFERER % referer.geturl()
return self._reject(request, reason)
csrf_token = request.META.get('CSRF_COOKIE')
if csrf_token is None:
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
# and in this way we can avoid all CSRF attacks, including login
# CSRF.
return self._reject(request, REASON_NO_CSRF_COOKIE)
# Check non-cookie token for match.
request_csrf_token = ""
if request.method == "POST":
try:
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
except IOError:
# Handle a broken connection before we've completed reading
# the POST data. process_view shouldn't raise any
# exceptions, so we'll ignore and serve the user a 403
# (assuming they're still listening, which they probably
# aren't because of the error).
pass
if request_csrf_token == "":
# Fall back to X-CSRFToken, to make things easier for AJAX,
# and possible for PUT/DELETE.
request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')
request_csrf_token = _sanitize_token(request_csrf_token)
if not _compare_salted_tokens(request_csrf_token, csrf_token):
return self._reject(request, REASON_BAD_TOKEN)
return self._accept(request)
def process_response(self, request, response):
if not getattr(request, 'csrf_cookie_needs_reset', False):
if getattr(response, 'csrf_cookie_set', False):
return response
if not request.META.get("CSRF_COOKIE_USED", False):
return response
# Set the CSRF cookie even if it's already set, so we renew
# the expiry timer.
self._set_token(request, response)
response.csrf_cookie_set = True
return response
三丶JSON
什么是JSON
1.JSON指的是JavaScript对象表示
2.JSON是轻量级的文本数据交换格式
3.JSON独立于语言
4.JSON具有自我描述性,更容易理解
### JSON的语法, json本质还是一个字符串
# JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名。
# JSON是一个序列化的对象或数组。
JSON数据
# 合格的json数据 必须是 双引号.
["one", "two", "three"]
{ "one": 1, "two": 2, "three": 3 }
{"names": ["张三", "李四"] }
[ { "name": "张三"}, {"name": "李四"} ]
# 不合格的json数据
{ name: "张三", 'age': 32 } // 属性名必须使用双引号
[32, 64, 128, 0xFFF] // 不能使用十六进制值
{ "name": "张三", "age": undefined } // 不能使用undefined
{ "name": "张三",
"birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
"getName": function() {return this.name;} // 不能使用函数和日期对象
}
和XML技术相比
JSON 格式于2001年由 Douglas Crockford 提出,目的就是取代繁琐笨重的 XML 格式。
JSON 格式有两个显著的优点:书写简单,一目了然;符合 JavaScript 原生语法,可以由解释引擎直接处理,不用另外添加解析代码。所以,JSON迅速被接受,已经成为各大网站交换数据的标准格式,并被写入ECMAScript 5,成为标准的一部分。
XML和JSON都使用结构化方法来标记数据,下面来做一个简单的比较.
# JSON 简单的语法格式和清晰的层次结构明显要比 XML 容易阅读,并且在数据交换方面,由于 JSON 所使用的字符要比 XML 少得多,可以大大得节约传输数据所占用得带宽
四丶ajax技术(JQ实现)
发送请求的方式
# 1. 浏览器输入地址 发送get请求
# 2. a标签 发送get请求
# 3. form表单,默认是get请求, 可以指定发送请求的方式 method
# 4. 异步发送 ajax js技术,type指定发送请求的方式
什么是ajax
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。
# 1.同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
# 2.异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
AJAX的优缺点
### 优点:
1.AJAX使用JavaScript技术向服务器发送异步请求;
2.AJAX请求无须刷新整个页面;
3.因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高
4.前端与后端负载均衡 将一些后端的工作移到前端,减少服务器与带宽的负担
5.异步与服务器通信 使用异步的方式与服务器通信,不打断用户的操作
6.
### 缺点:
1.Ajax干掉了Back与History功能,即对浏览器机制的破坏
在动态更新页面的情况下,用户无法回到前一页的页面状态,因为浏览器仅能记忆历史纪录中的静态页面
2.安全问题
AJAX技术给用户带来很好的用户体验的同时也对IT企业带来了新的安全威胁,Ajax技术就如同对企业数据建立了一个直接通道。这使得开发者在不经意间会暴露比以前更多的数据和服务器逻辑。
3.对搜索引擎支持较弱
4.破坏程序的异常处理机制
5.违背URL与资源定位的初衷
6.不能很好地支持移动设备
7.客户端肥大,太多客户段代码造成开发上的成本
## 如果网速慢,则会出现ajax请求缓慢,页面空白的情况,对客户的体验不好。ajax请求不利于搜索引擎优化,一般搜不到ajax添加到页面的信息!
## 解决的办法:可以先用服务器渲染。
jQuery实现的AJAX
import json # json模块
from django.http import JsonResponse # 直接转换成JSON对象
def ajax_jq(request):
if request.method=='POST':
text=request.POST.get('text')
if len(text)>0:
json1=json.dumps({'name':'xixi'}) # 序列化
return HttpResponse('OK') # 发送一个普通的HTTPResponse对象,普通的字符串
return HttpResponse(json1) # 发送的是一个json序列化的字符串
return JsonResponse({'name':'xixi2'}) # 直接发送的就是json数据
return render(request,'ajax.html')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
</head>
<body>
{% csrf_token %}
<input type="text" id="te">
<button>提交</button>
<script src="{% static 'js/jquery-1.11.1.min.js' %}"></script>
<script>
$('button').click(function () {
$.ajax({ // JQ 已经封装好了ajax的请求. 调用 ajax方法即可.
url: '/ajax_jq/', //发送的目标url地址
type: 'post', //发送方式
data: { //发送的数据
'csrfmiddlewaretoken': $('input[name="csrfmiddlewaretoken"]').val(), //由于存在csrf校验,必须携带csrfmiddlewaretoken的值
'text': $('#te').val(), //自定义文本框数据
},
success: function (res) { // 回调函数 ,res是结果, 一般是json数据
res=JSON.parse(res) // 反序列化json数据
console.log(res)
}
})
})
</script>
</body>
</html>
JS实现AJAX
var b2 = document.getElementById("b2");
b2.onclick = function () {
// 原生JS
var xmlHttp = new XMLHttpRequest(); //得到一个XML对象
xmlHttp.open("POST", "/ajax_test/", true); //
xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); //设置类型
xmlHttp.send("username=q1mi&password=123456"); //发送请求
xmlHttp.onreadystatechange = function () { //回调函数
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
alert(xmlHttp.responseText);
}
};
};
AJAX请求如何设置csrf_token
1.通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送。
<script>
$('button').click(function () {
$.ajax({
url: '/ajax_jq/',
type: 'post',
data: {
'csrfmiddlewaretoken': $('input[name="csrfmiddlewaretoken"]').val(), //由于存在csrf校验,必须携带csrfmiddlewaretoken的值
'text': $('#te').val(),
},
success: function (res) { // 回调函数 ,res是结果, 一般是json数据
res=JSON.parse(res) // 反序列化json数据
console.log(res)
}
})
})
</script>
2.通过获取返回的cookie中的字符串 放置在请求头中发送 (加请求头 x-csrftoken)
$('#btn2').click(function () {
console.log($.cookie('csrftoken'))
$.ajax({
url: '/ajax_csrf/', //发送的目标url地址
type: 'post', //发送方式
headers: {'X-CSRFToken': $.cookie('csrftoken')}, // 从cookie中获取csrftoken的值 , 需要插件js.cookies
headers: {'X-CSRFToken': $('input[name="csrfmiddlewaretoken"]').val()}, // 从页面中获得csrf的csrfmiddlewaretoken的值
data: { //发送的数据
'text': $('#te').val(), //自定义文本框数据
},
success: function (res) {
console.log(res)
}
})
})
3.导入jquery.cookie.js插件 或者 自定义getCookie方法
// ajaxSetup 设置全局的ajax, 自动获取csrf提交. 不用重复造轮子
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);
}
}
});