如何使用 Django REST 框架制作 POST 简单的 JSON? CSRF 令牌丢失或不正确

Posted

技术标签:

【中文标题】如何使用 Django REST 框架制作 POST 简单的 JSON? CSRF 令牌丢失或不正确【英文标题】:How to make a POST simple JSON using Django REST Framework? CSRF token missing or incorrect 【发布时间】:2013-07-04 15:19:18 【问题描述】:

如果有人向我展示如何使用带有 Django REST 框架的 JSON 发出简单的 POST 请求,我将不胜感激。我在教程中的任何地方都没有看到任何这样的例子?

这是我要发布的角色模型对象。这将是一个全新的角色,我想将它添加到数据库中,但出现 500 错误。


    "name": "Manager", 
    "description": "someone who manages"

这是我在 bash 终端提示符下的 curl 请求:

curl -X POST -H "Content-Type: application/json" -d '[

    "name": "Manager", 
    "description": "someone who manages"
]'


http://localhost:8000/lakesShoreProperties/role

网址

http://localhost:8000/lakesShoreProperties/roles

可以使用 GET 请求,我可以提取数据库中的所有角色,但我似乎无法创建任何新角色。我没有设置权限。我在views.py中使用标准视图

class RoleDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Role.objects.all()
    serializer_class = RoleSerializer
    format = None

class RoleList(generics.ListCreateAPIView): 
        queryset = Role.objects.all()
        serializer_class = RoleSerializer
        format = None

在我的urls.py 中,相关的 url - 视图映射是正确的:

url(r'^roles/$', views.RoleList.as_view()),
url(r'^role/(?P<pk>[0-9]+)/$', views.RoleDetail.as_view()),

错误信息是:


    "detail": "CSRF Failed: CSRF token missing or incorrect."

这里发生了什么,对此有什么解决方法? localhost 是跨站点请求吗?我已将@csrf_exempt 添加到RoleDetailRoleList,但它似乎并没有改变任何东西。这个装饰器甚至可以添加到类中,还是必须添加到方法中? 添加@csrf_exempt装饰,我的错误变成:

Request Method: POST
Request URL:    http://127.0.0.1:8000/lakeshoreProperties/roles/
Django Version: 1.5.1
Exception Type: AttributeError
Exception Value:    
'function' object has no attribute 'as_view'

然后我在整个应用程序中禁用了 CSRF,现在我收到以下消息:

"non_field_errors": ["Invalid data"] 当我知道我的 JSON 对象是有效的 json 时。这是一个非字段错误,但我被困在这里。

嗯,原来我的json无效?


    "name": "admin", 
    "description": "someone who administrates"

[
    
        "name": "admin",
        "description": "someone who administrates"
    
]

使用括号 [] 会导致 POST 请求失败。但是使用 jsonlint.com 验证器,我的两个 json 对象都会验证。

更新:问题在于使用 PostMan 发送 POST,而不是在后端。见https://***.com/a/17508420/203312

【问题讨论】:

RoleSerializer 是您定义的吗?也许值得研究。 【参考方案1】:

提供当前状态的更新,并总结一些答案:

在与其交互的 API 相同的上下文中发出的 AJAX 请求通常使用SessionAuthentication。这可确保一旦用户登录,任何 AJAX 请求都可以使用与网站其余部分相同的基于会话的身份验证进行身份验证。

在与其通信的 API 不同的站点上发出的 AJAX 请求通常需要使用非基于会话的身份验证方案,例如 TokenAuthentication

因此,建议将SessionAuthentication 替换为TokenAuthentication 的答案可能会解决问题,但不一定完全正确。

要防范此类攻击,您需要做两件事:

    确保“安全”HTTP 操作(例如 GETHEADOPTIONS)不能用于更改任何服务器端状态。

    确保任何“不安全”的 HTTP 操作,例如 POSTPUTPATCHDELETE,始终需要有效的 CSRF 令牌。 如果您使用SessionAuthentication,则需要为任何POSTPUTPATCHDELETE 操作包含有效的CSRF 令牌。

为了发出 AJAX 请求,您需要在 HTTP 标头中包含 CSRF 令牌,如 Django 文档中所述。

因此,将 csrf 包含在 header 中很重要,例如this answer 建议。

参考:Working with AJAX, CSRF & CORS, Django REST framework documentation。

【讨论】:

【参考方案2】:

您可能需要随请求一起发送 CSRF 令牌。查看https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/#csrf-ajax

更新:因为您已经尝试过豁免 CSRF,也许这会有所帮助(取决于您使用的 Django 版本):https://***.com/a/14379073/977931

【讨论】:

谢谢。看起来 CSRF 现在对我来说只是一个伤害包。禁用使我的 POST 请求现在可以正常工作。我想保持 CSRF 处于启用状态,但它与 Django REST Framework 不兼容。 avilpage.com/2019/02/django-tips-csrf-token-postman-curl.html 帮我解决了这个问题!【参考方案3】:

如果您设置了 AllowAny 权限并且您面临 csrf 问题

REST_FRAMEWORK = 
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny'
    ]

然后在settings.py 中添加以下内容将解决问题

REST_SESSION_LOGIN = False

【讨论】:

【参考方案4】:

旧的Postman 在使用 csrf 令牌时遇到问题,因为它不能使用 cookie。

我建议您切换到新版本的postman,它适用于cookie,您将不会再遇到这个问题。

【讨论】:

【参考方案5】:

它来自您的 REST 框架设置。在您的settings.py 文件中,您的REST_FRAMEWORK 应具有以下内容。

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

这会将您的 REST 框架设置为使用令牌身份验证而不是 csrf 身份验证。并且通过将权限设置为AllowAny,您可以只在您想要的地方进行身份验证。

【讨论】:

这也解决了我的问题。我使用rest_framework.permissions.IsAuthenticated 而不是AllowAny,效果很好。【参考方案6】:

正如你所说,你的网址是

http://localhost:8000/lakesShoreProperties/roles

Postman 的 localhost 存在一些问题。 将 POST 发送到 127.0.0.1:8000/your-api/endpoint 反而对我有用。

【讨论】:

【参考方案7】:

在 Django REST Framework 中,CSRF 是默认豁免的。因此,curl POST 请求可以正常工作。 POSTMAN 请求调用返回的 CSRF 不正确,因为如果在 Cookies 中找到 POSTMAN 包含 csrf 令牌。您可以通过清理 Cookie 来解决此问题。

【讨论】:

感谢邮递员的评论!对我来说,这完全是 cookie 造成的 :)【参考方案8】:

好的,现在我当然收回我说的话。 CSRF 确实按预期工作。

我正在使用名为 POSTMAN 的 chrome 插件发出 POST 请求。 启用 CSRF 后,我的 POST 请求失败。

但是使用

的 curl POST 请求
curl -X POST -H "Content-Type: application/json" -d '

    "name": "Manager",
    "description": "someone who manages"
' http://127.0.0.1:8000/lakeshoreProperties/roles/

工作正常... 我不得不取下大括号,即 [],并确保角色中的 's' 后面有一个斜线,即角色/,并且启用 csrf 并没有引发任何错误。

我不确定使用 POSTMAN 调用与使用 curl 的区别是什么,但 POSTMAN 是在 Web 浏览器中运行的,这是最大的区别。也就是说,我为整个类 RoleList 禁用了 csrf,但一个相同的请求适用于 Curl,但使用 POSTMAN 失败。

【讨论】:

以上是关于如何使用 Django REST 框架制作 POST 简单的 JSON? CSRF 令牌丢失或不正确的主要内容,如果未能解决你的问题,请参考以下文章