drf(十二)解析drf通过csrf验证
drf(十二)解析drf通过csrf验证
问题引出:django 网站中的发送 post 请求时会被加密,如果不设置会无法请求。
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')
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")
成功通过!
第二种:直接装饰类,实际上也是装饰了改方法,只是将该方法的名成作为装饰参数;
-
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")
总结:通过装饰器修饰函数实现视图通过 csrf_token,在drf前戏一文中,CBV 是通过as_view()函数返回 view 函数后其中执行 dispatch 方法,因此本质上仍是执行装饰器修饰函数的过程;
3.drf通过csrf
3.1 查看APIView中的方法
每个路由对应as_view()方法,查看源码
通过图片可知源码中也导入了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()方法
3.2 装饰器知识的补充
def outer(origin):
def inner():
print("啊哈哈")#函数执行前
res=origin()
print("啊哈哈哈")#函数执行后
return inner # 返回原函数并且不带括号
def func():#
print("aaa")
第一种(常规)调用
@outer
def func():#
print("aaa")
func()
第二种方式
f=outer(func)
f()
# 可知 装饰器函数 只要加上(),就会执行,相当于使用@执行装饰器一样。
根据运行结果可知结果相同。
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 关于跨域的知识。
继续努力,终成大器!