drf(十二)解析drf通过csrf验证

drf(十二)解析drf通过csrf验证

问题引出:django 网站中的发送 post 请求时会被加密,如果不设置会无法请求。

image-20220412174206846

1.什么是csrf

  • 跨站请求伪造(CSRF)与跨站请求脚本正好相反。跨站请求脚本的问题在于,客户端信任服务器端发送的数据。跨站请求伪造的问题在于,服务器信任来自客户端的数据。

配置文件中的配置

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware', # csrf的配置
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

在 django 中 csrf 的本质上是一个中间件!

2.通过csrf

2.1 django中的设置法

说明:本部分不做过多解释,重点在介绍drf中的源码解析

  • 方法一:注释掉中间中的配置;(调试时可以使用,生产环境中不建议使用)
  • 方法二:from 表单中添加上{% CSRF_TOKEN %}
  • 方法三:ajax 使用 "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val();
  • 方法四:ajax中的参数data: $('#smsForm').serialize() .可以通过

2.2 装饰器

2.2.1 Fbv的装饰器

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt # 使用改装饰器进行装饰
def func(request):
    if request.method=="POST":
        return HttpResponse("...")
    return render(request,'form.html')

image-20220412195413846

2.2.2 CBV的装饰器

cbv 通过 csrf 验证的方式有两种;

第一种:装饰父类的dispatch()方法。

  • from django.utils.decorators import method_decorator
    from django.views.decorators.csrf import csrf_exempt
    class Func(View):
        @method_decorator(csrf_exempt)
        def dispatch(self, request, *args, **kwargs):
            return super(Func,self).dispatch(request, *args, **kwargs) # 执行父类的dispatch方法
    
        def get(self,request):
            return render(request,'form.html')
        def post(self,request):
            return HttpResponse("666666")
    

    image-20220412200528610

    成功通过!

第二种:直接装饰类,实际上也是装饰了改方法,只是将该方法的名成作为装饰参数;

  • from django.utils.decorators import method_decorator
    from django.views.decorators.csrf import csrf_exempt
    @method_decorator(csrf_exempt,name='dispatch')
    class Func(View):
        def get(self,request):
            return render(request,'form.html')
        def post(self,request):
            return HttpResponse("666666")
    

    image-20220412200844938

总结:通过装饰器修饰函数实现视图通过 csrf_token,在drf前戏一文中,CBV 是通过as_view()函数返回 view 函数后其中执行 dispatch 方法,因此本质上仍是执行装饰器修饰函数的过程;

3.drf通过csrf

3.1 查看APIView中的方法

每个路由对应as_view()方法,查看源码

image-20220412202022909

通过图片可知源码中也导入了csrf_exempt

class APIView(View):

    @classmethod
    def as_view(cls, **initkwargs):
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation

        view = super().as_view(**initkwargs) #执行父类中as_view()方法
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view) # 装饰器

父类中的as_view()方法

image-20220412202616039

3.2 装饰器知识的补充

def outer(origin):
    def inner():
        print("啊哈哈")#函数执行前
        res=origin()
        print("啊哈哈哈")#函数执行后
    return inner # 返回原函数并且不带括号

def func():#
    print("aaa")

第一种(常规)调用

@outer
def func():#
    print("aaa")
func()

image-20220412203052429

第二种方式

f=outer(func)
f()
# 可知 装饰器函数 只要加上(),就会执行,相当于使用@执行装饰器一样。

image-20220412204337808

根据运行结果可知结果相同。

3.3 csrf_exempt源码

def csrf_exempt(view_func):
    """Mark a view function as being exempt from the CSRF view protection."""
    # view_func.csrf_exempt = True would also work, but decorators are nicer
    # if they don't have side effects, so return a new function.
    def wrapped_view(*args, **kwargs):
        return view_func(*args, **kwargs)
    wrapped_view.csrf_exempt = True
    return wraps(view_func)(wrapped_view) #未返回嵌套的函数。

查看源码可知,csrf_exempt 是一个嵌套函数,但是闭包要求返回嵌套的函数。那么查看wraps

def wraps(wrapped,assigned = WRAPPER_ASSIGNMENTS,updated = WRAPPER_UPDATES):

    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)

查看源码可知,partial 是偏函数,相当于执行原来的函数。

偏函数,一般将已有的函数进行参数填充,并且生成新的函数。

import functools

def f1(a1,a2):
    print(a1,a2)

new_func=functools.partial(f1,123)# 生成新的函数
new_func(2) # 将这个参数传递给v2
# >>> 123 2

def f1(a1): # 一个参数时,只是生成一个新函数
    print(a1)

new_func=functools.partial(f1)
new_func(2) 
# >>> 2


import functools

def f1(a1):
    print(a1)

new_func=functools.partial(f1)(3)
new_func # 直接执行函数 
# >>> 3 相当于执行 f1(3)

偏函数在flask中也应用,是把request或者session作为参数传递,生成不同的函数,应用很nice!此处 不做详细介绍。

回到源码中的最后一句

return wraps(view_func)(wrapped_view)
# wraps(view_func) 使用偏函数,返回一个新的函数,function
# function(wrapped_view) 相当于执行新函数加上参数
# 猜测: 相当于执行 view_func(wrapped_view)

# 本部分涉及一些底层语法,不再深入。

通过平常的使用和一些源码得知,csrf_exempt 当做装饰器使用。牢记后一句;

as_view()的返回值是csrf_exempt(view),通过该函数的作用直接通过csrf的验证;因此在drf的使用中可以不用做任何的操作,因为视图类继承了APIView

最后扩展:drf 一般会在前后端分离的项目中,而在前后端分离的项目中,一般会涉及到跨域,因此需要做跨域的配置。后期会更新,django 关于跨域的知识。

继续努力,终成大器!

posted @ 2022-04-12 22:04  紫青宝剑  阅读(581)  评论(0编辑  收藏  举报