zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    metadata的使用以及简单的orm模式
    python的cache修饰器
    聊天服务的设计随想
    cherrypy入门
    用python做分布式定时器
    cherrypy & gevent patch
    Python Tornado简单的http request
    连接池的一些感悟
    企业系统架构评估标准
    Nginx与python web服务配置(Uwsgi& FastCGI)
  • 原文地址:https://www.cnblogs.com/dengz/p/11494920.html
Copyright © 2011-2022 走看看