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是一个序列化的对象或数组。

img

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);
    }
  }
});

https://www.cnblogs.com/maple-shaw/articles/9524153.html

posted @ 2019-09-09 23:10  染指未来  阅读(439)  评论(0编辑  收藏  举报