Django-rest-framework: 默认情况下,DRF 应授权所有 OPTIONS 请求

创建于 2017-11-21  ·  22评论  ·  资料来源: encode/django-rest-framework

继 #908 的讨论后

OPTIONS (元数据)请求的权限在 DRF 中处理不正确。

根据W3C 规范,所有预检OPTIONS请求都未经过身份验证,这意味着用户在预检对经过身份验证的端点的请求时将始终收到 401 错误,因为现代浏览器从不按照规范发送:

否则,发出预检请求。 使用引用源作为覆盖引用源并设置手动重定向标志和阻止 cookie 标志,使用OPTIONS方法从源源获取请求 URL,并具有以下附加约束:

  • 包含一个 Access-Control-Request-Method 标头,其中包含请求方法的标头字段值(即使这是一个简单的方法)。
  • 如果作者请求标头不为空,则包含一个 Access-Control-Request-Headers 标头,标头字段值为逗号分隔的标头字段名称列表,按字典顺序来自作者请求标头,每个标头都转换为 ASCII 小写字母(即使有一个或更多是一个简单的标题)。
  • 排除作者请求标头。
  • ➡️排除用户凭据
  • 排除请求实体正文。

我认为这应该是这里的默认行为。
默认情况下,DRF 应为标准权限类(IsAuthenticated、IsAdminUser 等)授权所有OPTIONS请求,当用户明确需要保护元数据信息(违反标准 CORS 兼容性)时,他们可以覆盖这一点

重现步骤 :

视图.py

class MyView(APIView):

    permission_classes = (IsAuthenticated,)

网址.py

urlpatterns = [
    url(r'^myview/', MyView.as_view()),
]

安慰

http GET http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

http OPTIONS http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

预期行为

http GET http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

http OPTIONS http://127.0.0.1:8000/myview/
HTTP/1.0 200 OK

临时解决办法

class IsAuthenticated(permissions.IsAuthenticated):

    def has_permission(self, request, view):
        if request.method == 'OPTIONS':
            return True
        return super(IsAuthenticated, self).has_permission(request, view)

最有用的评论

DRF 版本:3.7.3
蟒蛇 3.6.3

上面提到的临时解决方法对我不起作用。 所有请求都经过身份验证,无论它们是 PUT、POST、GET、OPTIONS 等。

它致力于将其更改为:

# myapp/permissions.py
from rest_framework.permissions import IsAuthenticated

class AllowOptionsAuthentication(IsAuthenticated):
    def has_permission(self, request, view):
        if request.method == 'OPTIONS':
            return True
        return request.user and request.user.is_authenticated

在 settings.py 中:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.TokenAuthentication',),
    'DEFAULT_PERMISSION_CLASSES': (
        'myapp.permissions.AllowOptionsAuthentication',
    )
}

所有22条评论

DRF 版本:3.7.3
蟒蛇 3.6.3

上面提到的临时解决方法对我不起作用。 所有请求都经过身份验证,无论它们是 PUT、POST、GET、OPTIONS 等。

它致力于将其更改为:

# myapp/permissions.py
from rest_framework.permissions import IsAuthenticated

class AllowOptionsAuthentication(IsAuthenticated):
    def has_permission(self, request, view):
        if request.method == 'OPTIONS':
            return True
        return request.user and request.user.is_authenticated

在 settings.py 中:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.TokenAuthentication',),
    'DEFAULT_PERMISSION_CLASSES': (
        'myapp.permissions.AllowOptionsAuthentication',
    )
}

我也遇到了同样的问题。 我同意建议的解决方案,并感谢您的临时解决方法!

并非 100% 确信“默认情况下应授权所有 OPTIONS 请求”正是我们想要的行为,因为有很多使用 REST 框架的开发人员将OPTIONS请求用于 CORS 预检请求以外的情况。

但是,我认为我们可能确实希望拥有一个受祝福的CORS 选项。 我不知道这是否意味着我们应该直接包含https://github.com/ottoyiu/django-cors-headers/ ,或者我们是否应该更高度地引用它。

使用该软件包后此问题是否解决?

这个包并不是真正的解决方案,它只是附加了 CORS 标头,但没有解决 OPTIONS 返回 HTTP401 的事实,如果 DRF 权限没有专门授予所有未经身份验证的请求。

IMO 应该以相反的方式引入它,即默认情况下允许并带有中断更改通知。
这是根据 W3C 规范的预期行为,并且在文档中提到第 3 方来修补错误的实现并不是一个真正干净的解决方案......

因此,尽管有人将 OPTIONS 用于非 CORS 相关的事物(我就是其中之一),但由于越来越多的浏览器正在强制执行 CORS 机制,因此可能会有更多人隐含地期待开箱即用。 事实上,这些机制可能会通过网络生成 99% 以上的 OPTIONS 请求。

有趣的冲突。 我完全理解默认情况下在 OPTIONS 方法上有一个隐式允许的问题。 我自然更倾向于更安全的解决方案,我想这取决于开发人员对 DRF 的目的。 所以默认拒绝对我来说似乎更合适。

允许 OPTIONS 的 W3C 规范也可能仅适用于 CORS 的上下文。 显然,DRF 无法知道上下文,应该是提供给开发人员的设置。

如果打算使用 CORS,则允许所有 OPTIONS 请求。 默认情况下,CORS 是非预期的。

实际上,我认为我们可以考虑对主要版本进行重大更改,而当前行为仍然可用。 我们现有的 OPTIONS 行为已经到位,因为人们会使用 JSONP 而不是 CORS,所以我们可能需要进行更新,删除我们公开的相当特别的默认信息集,而是默认仅专注于 CORS 的 OPTIONS .

+1

@tomchristie你知道什么时候会发生主要版本更新/修复吗? 我也在碰到这个问题。

同时,@ medakk的解决方法是完美的!

将此作为里程碑以确保它在 3.9 中得到适当考虑

另一种解决方法是在视图中明确处理此问题。 在您有多个 permission_classes 的情况下,这会更 DRY,因为它们中的每一个都需要这种处理。

def check_permissions(self, request):
        if request.method == 'OPTIONS':
            return
        super(MyApiView, self).check_permissions(request)

回到这个进一步研究它 - https://github.com/OttoYiu/django-cors-headers包处理中间件中预检的OPTIONS请求,并直接返回响应。 REST framework 在这里做什么并不重要,因为请求/响应在它到达 REST framework 之前就被拦截了。

对我来说,我们这里问题并不明显。

预检响应不涉及 REST 框架: https :

谢谢。 我们没有使用 django-cors-headers,但也许我们应该使用。
我仍然同意之前的评论,即 DRF 不需要外部包来处理符合 W3C 的 CORS 请求。

是的,有很多方式/第 3 方包可以规避这个问题。
但同样,由于 DRF 与 W3C CORS 规范不兼容,因此已创建此票证,应予以修复。

如今,对端点执行的绝大多数OPTIONS请求都是出于预检目的,对于新的 DRF 用户来说,由于 DRF 固执的选择而不得不手动处理这些问题可能具有欺骗性。

如今,对端点执行的绝大多数OPTIONS请求都是出于预检目的,对于新的 DRF 用户来说,由于 DRF 固执的选择而不得不手动处理这些问题可能具有欺骗性。

仅仅因为 CORS 劫持了 OPTIONS 方法并不意味着默认情况下应该更改其安全性以适应。 特别是如果正确实施了 CORS 支持,它将按照需要的方式工作。 抱歉,但我认为想要更改此设置的开发人员需要检查自己的意见,而不是 DRF 维护人员。

也许文档增强建议如果需要 CORS,则使用帮助正确实施的模块而不是重新发明轮子。

让我们降低水平。

中间件处理 CORS 标头的明智之选。 我们可以将它构建到 REST 框架中,但是现有的包已经很好地涵盖了它。

REST framework 碰巧为OPTIONS请求返回的主体在这里无关紧要,因为预检的 CORS 请求无论如何都应该被中间件拦截,并且标准的 CORS 请求可以是任何 HTTP 方法。

我很高兴 REST 框架默认提供这些响应主体,但这与 CORS 支持问题略有不同,并不是 REST 框架在那里有自以为是的行为,而是它有早于 CORS 被广泛实施的行为。

在我看来,仅靠中间件解决方案是不够的,或者至少需要 DRF 的一些合作。 如果您查看引用的代码,您会发现使用全局默认值来提供 CORS 标头。 但是我如何全局知道每个视图支持哪些标头?

因此,我希望看到中间件从视图集/视图中连接到这些配置选项的标准方法:例如allowed_cors_headers方法。

我同意这不需要由视图处理,但它绝对需要尊重视图对它可以提供什么的知识。

将路由器级覆盖应用于路由器中的所有视图也可能是值得的。

其他注意事项:

  • 因产地而异
  • 因内容类型而异

这不是我最喜欢的选择,但趋势是使用 3rd-party。

我认为我们可以在文档更新为 CORS 用户的正确信息后关闭此票证(例如:“请注意,DRF 不满足 W3C CORS 对 OPTIONS 请求的规范。如果您使用 CORS 的 OPTIONS,请不要忘记添加适当的中间件,否则您的预检请求可能会因缺乏授权而失败”等)

现有文档https://www.django-rest-framework.org/topics/ajax-csrf-cors/#cors是完全合理的,在中间件中处理 CORS 在任何情况下都是正确的方法。

但是我如何全局知道每个视图支持哪些标头?

你不需要 - 你需要知道网站的 CORS 政策是什么。

很高兴接受拉取请求,使 CORS 文档更加突出,或者将它们包含在其他合适的地方。 除此之外,这里没有可操作的问题。

我对规范的误解,哎呀。

在这种情况下,支持最好包含在 django 站点 contrib 中
包,而不是drf。

勒马尔。 19 热。 2019 年 10:22 am,Tom Christie [email protected] a
评论:

现有的文件
https://www.django-rest-framework.org/topics/ajax-csrf-cors/#cors
完全合理,在中间件中处理 CORS 才是正确的
在任何情况下的方法。

但是我如何全局知道每个视图支持哪些标头?

你不需要 - 你需要知道网站的 CORS 政策是什么。


您收到此消息是因为您发表了评论。
直接回复本邮件,在GitHub上查看
https://github.com/encode/django-rest-framework/issues/5616#issuecomment-465146969
或静音线程
https://github.com/notifications/unsubscribe-auth/AFhtlM6bG-Bs2CvoO972pIfwvxLHbzAxks5vPAjAgaJpZM4Qlvkn
.

此页面是否有帮助?
0 / 5 - 0 等级