使用 PUT/DELETE 方法休息框架的“CSRF 令牌丢失”

Posted

技术标签:

【中文标题】使用 PUT/DELETE 方法休息框架的“CSRF 令牌丢失”【英文标题】:"CSRF token missing" with PUT/DELETE meethod rest-framework 【发布时间】:2017-01-07 16:59:19 【问题描述】:

我正在使用带有 ModelViewSet 的 django rest 框架可浏览 api 来执行 CRUD 操作并希望使用权限。IsAuthenticatedOrReadOnly,但是当我登录并尝试 DELETE 或 PUT 时,我得到了 "detail": "CSRF Failed: CSRF token missing or incorrect."

我的视图是这样的

class objViewSet(viewsets.ModelViewSet):
    queryset = obj.objects.all()
    serializer_class = objSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

Settings.py

REST_FRAMEWORK = 
'DEFAULT_PERMISSION_CLASSES': (
    'rest_framework.permissions.AllowAny',
),

序列化器只是

class ObjSerializer(serializers.ModelSerializer):
    class Meta:
        model = Obj

虽然当我删除 permission_classes(所以默认的 allowAny 触发器)时,它可以正常工作。

我想要什么

只有在我通过身份验证后才能进行 PUT/DELETE。当一切自动发生时,我不知道如何发送 CSRF 令牌(modalviewset 完成整个工作)

【问题讨论】:

您想全局删除 csrf 检查吗?还是单独的非 csrf 视图? 如果您在这里找不到更好的解决方案,我敢打赌您正在使用“rest_framework.authentication.SessionAuthentication”作为身份验证类之一。从设置中删除它,一切都应该正常工作。 【参考方案1】:

在您的 REST_FRAMEWORK 设置中,您没有提到身份验证方案,因此 DRF 使用默认身份验证方案,即 SessionAuthentication。此方案强制要求您将 csrf 令牌与您的请求一起放置。你可以通过以下方式克服这个问题:

    在 settings.py 中为整个项目进行此设置添加


    REST_FRAMEWORK = 
        'DEFAULT_AUTHENTICATION_CLASSES': (
             'rest_framework.authentication.BasicAuthentication',
        )

    要在特定视图中进行此设置,请在您的视图中执行以下操作


    class objViewSet(viewsets.ModelViewSet):
        queryset = obj.objects.all()
        serializer_class = objSerializer
        permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
        authentication_classes = (BasicAuthentication,)

来源:http://www.django-rest-framework.org/api-guide/authentication/#sessionauthentication

顺便说一句,csrf 令牌保存为名为“csrftoken”的 cookie。您可以从 HTTP 响应中检索它,并使用密钥“X-CSRFToken”将其附加到您的请求标头。您可以在以下位置查看一些详细信息:https://docs.djangoproject.com/en/dev/ref/csrf/#ajax

【讨论】:

【参考方案2】:

您可能一直对 csrf_token 使用 SessionAuthentication 和会话身份验证检查,您可以通过豁免来避免检查,但我们不要这样做。

我认为您可以保留两个身份验证类。或者只是 TokenAuthentication。即,

'DEFAULT_AUTHENTICATION_CLASSES': (
    # 'rest_framework.authentication.BasicAuthentication',
    'rest_framework.authentication.TokenAuthentication',
    'rest_framework.authentication.SessionAuthentication',

    # 'oauth2_provider.ext.rest_framework.OAuth2Authentication',
    # 'rest_framework_social_oauth2.authentication.SocialAuthentication',
),

如果您不打算使用 TokenAuth 而只是会话身份验证。您始终可以通过 X-CSRFToken 标头传递 csrf_token 。或者你可以去 csrf_except 东西。这将避免 csrf 丢失问题。

这应该可行。另请参阅下面的链接。

参考:https://***.com/a/26639895/3520792

参考:https://***.com/a/30875830/3520792

【讨论】:

“你总是可以通过 X-CSRFToken 标头传递 csrf_token”这正是我不知道该怎么做,因为我正在使用 ModelSetView 这与您使用的视图无关。您正在使用会话,服务器没有从客户端获取 csrf_token。您只需在 X-CSRFToken 请求标头中传递 csrf_token 。有一些方法可以在客户端获取 csrf_token。 ref:- ***.com/questions/22063612/… , ***.com/questions/28417781/… 调查一下。它只是您的请求标头应该有它。 我知道如何使用 ajax 从客户端执行此操作。但是我的客户端(可浏览的 api)是由 django 自动生成的,这意味着 - 我无权访问它。是否有任何选项可以在 django 代码中修改我的视图(因此它们包括 CSRFToken),或者我是否必须覆盖这个 borwsable api 视图,然后在单击“PUT”/“DELETE”时使用 ajax? 我在本地也有同样的工作。我刚刚从 Django REST framework 3.2.4 提供的可浏览 API 接口检查了 API put post。它为我工作。使用答案中给出的设置。如果您正在做任何不同的事情,请帮助我重现它。【参考方案3】:

您应该将 CSRF 令牌添加到您的请求中。如果您使用 JSON 请求执行此操作,则应将其添加到您的 JS 代码中。 它从用户 cookie 添加 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);
        
    
);

【讨论】:

【参考方案4】:

您可以删除对单个 url 的 CSRF 检查。在你的 urls.py 上试试这个:

url(r'^/my_url_to_view/', csrf_exempt(views.my_view_function), name='my_name'),

【讨论】:

以上是关于使用 PUT/DELETE 方法休息框架的“CSRF 令牌丢失”的主要内容,如果未能解决你的问题,请参考以下文章

Chrome 的插件 CORS Toggle 无法使用 POST/PUT/DELETE 方法

使用Spring boot开发RestFul 风格项目PUT/DELETE方法不起作用

nginx proxy_pass基于请求方法是POST,PUT还是DELETE

Ajax中Put和Delete请求传递参数无效的解决方法(Restful风格)

Express 无法 PUT/DELETE 方法。出了啥问题?

可以发送 PUT/DELETE 方法的负载测试工具