zoukankan      html  css  js  c++  java
  • Django-csrf中间件

    一、详解csrf原理

    csrf要求发送post,put,或者delete请求的时候,是先以get方式发送请求,服务端响应时会分配一个随机字符串给客户端,客户端第二次发送post,put或delete请求的时携带上次分配的字符串到服务端进行校验。(大白话:用户开始的时候请求网站是发GET请求,之后服务端CSRF会携带一个随机字符型给客户端,当用户提前POST或put、delete请求的时候CSRF会校验随机字符串是不是合法的。)

    二、Django中间件的CSRF

    首先,我们知道Django中间件作用于整个项目。

    在一个项目中,如果想对全局所有视图函数或视图类起作用时,就可以在中间件中实现,比如想实现用户登录判断,用户访问频率,基于用户的权限管理(RBAC)等都可以在Django中间件中来进行操作。

    三、Django中间件的CSRF的源码解析:

    class CsrfViewMiddleware(MiddlewareMixin):
    
        def _accept(self, request):
            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,
                )
                patch_vary_headers(response, ('Cookie',))
    
        def process_request(self, request):
            csrf_token = self._get_token(request)
            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
    
            if getattr(callback, 'csrf_exempt', False):
                return None
    
            if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
                if getattr(request, '_dont_enforce_csrf_checks', False):
                    return self._accept(request)
    
                if request.is_secure():
                    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)
    
                    if '' in (referer.scheme, referer.netloc):
                        return self._reject(request, REASON_MALFORMED_REFERER)
    
                    if referer.scheme != 'https':
                        return self._reject(request, REASON_INSECURE_REFERER)
    
                    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:
                        good_referer = request.get_host()
    
                    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:
                    return self._reject(request, REASON_NO_CSRF_COOKIE)
    
                request_csrf_token = ""
                if request.method == "POST":
                    try:
                        request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
                    except IOError:
                        pass
    
                if request_csrf_token == "":
                    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
    
            self._set_token(request, response)
            response.csrf_cookie_set = True
            return response
    Django中间件CSRF源码

    从上面的源码找那个可以看到,CsrfViewMiddleware中间件中定义process_request,prcess_view和process_response三个方法

    首先我们来看下process_request方法:

    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 process_request(self, request):  
            csrf_token = self._get_token(request)  
            if csrf_token is not None:  
                # Use same token next time.  
          request.META['CSRF_COOKIE'] = csrf_token

    从Django项目配置文件夹中读取CSRF_USE_SESSIONS的值,如果获取成功,则从session中读取CSRF_SESSION_KEY的值,默认为'_csrftoken',如果没有获取到CSRF_USE_SESSIONS的值,则从发送过来的请求中获取CSRF_COOKIE_NAME的值,如果没有定义则返回None。

    再来看process_view方法

    在process_view方法中,先检查视图函数是否被csrf_exempt装饰器装饰,如果视图函数没有被csrf_exempt装饰器装饰,则程序继续执行,否则返回None。接着从request请求头中或者cookie中获取携带的token并进行验证,验证通过才会继续执行与URL匹配的视图函数,否则就返回403 Forbidden错误。

    四、在实际项目中,会在发送POST,PUT,DELETE,PATCH请求时,在提交的form表单中添加{% csrf_token%}

    <form action="" method="post">
        {% csrf_token %}
        <p>username:<input type="text" name="username"></p>
        <p>target_account:<input type="text" name="target_user"></p>
        <p>money:<input type="text" name="money"></p>
        <input type="submit">
    </form>
    
    #通过浏览器查看我们课一看到它给我们加了name="csrfmiddlewaretoken" value="rJ47FeK9T55wavvVJGY6UxdM1kTMHhTqotGfaXjXIK8Ahz2Uvs02yR9T8bBn5q2D"。
    #如下面标签
    <input type="hidden" name="csrfmiddlewaretoken" value="rJ47FeK9T55wavvVJGY6UxdM1kTMHhTqotGfaXjXIK8Ahz2Uvs02yR9T8bBn5q2D">

    五、在遇到ajax的时候我们如何解决呢:

    1.方式一:

    #先在页面任意的位置上书写{% csrf_token %}
    #然后在发送ajax请求的时候 通过标签查找获取随机字符串添加到data自定义对象即可
    data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},

    2.方式二:

    data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},        

    3.方式三:

    直接建一个js文件,拷贝代码,导入即可,你不需要做任何的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);
        }
      }
    });

    六、CSRF相关的装饰器:

    1.第一种情况:

    from django.views.decorators.csrf import csrf_exempt,csrf_protect
    # @csrf_exempt  # 给谁装饰就不校验谁 csrf
    def index(request):
        return HttpResponse('index')
    
    
    @csrf_protect  # 给谁装饰就给谁校验csrf
    def login(request):
        return HttpResponse('login')

    2.第二种情况:

    #当这两个装饰器在CBV上有何异同
    # @method_decorator(csrf_exempt,name='post')  # csrf_exempt不支持该方法加装饰器
    @method_decorator(csrf_exempt,name='dispatch')  # csrf_exempt这种方法支持
    class MyIndex(views.View):
        # @method_decorator(csrf_exempt)  # 这种方法也是支持
        def dispatch(self, request, *args, **kwargs):
            return super().dispatch(request,*args,**kwargs)
        def get(self,request):
            return render(request,'transfer.html')
        # @method_decorator(csrf_exempt,name='post')  # csrf_exempt不支持该方法
        def post(self,request):
            return HttpResponse('OK')        
    # csrf_exempt这个装饰器只能给dispatch装才能生效!!!!

    3.第三种情况:

    csrf_protect方式全都可以  跟你普通的装饰器装饰CBV一致!

    class MyIndex(views.View):
        @method_decorator(csrf_protect)
        def dispatch(self, request, *args, **kwargs):
            return super().dispatch(request,*args,**kwargs)
        def get(self,request):
            return render(request,'transfer.html')
        # @method_decorator(csrf_protect)  # 可以
        def post(self,request):
            return HttpResponse('OK')
  • 相关阅读:
    关于jQuery的选择器
    解读position定位
    html5新增的功能。
    关于ajax的同步异步
    响应式布局由来和剖析
    jQuery的效果函数及如何运用
    jQuery的选择器
    position定位的解析与理解
    HTML5与CSS3中新增的属性详解
    对Ajax的解析
  • 原文地址:https://www.cnblogs.com/mqhpy/p/11991853.html
Copyright © 2011-2022 走看看