【问题标题】:Django REST Framework pagination links do not use HTTPSDjango REST Framework 分页链接不使用 HTTPS
【发布时间】:2017-08-06 17:36:42
【问题描述】:

我正在为某个运行良好的 DRF 端点设置分页 - 但是,当部署在使用 HTTPS 的服务器上时,指向下一页和上一页的链接由 http:// 而不是 https:// 形成。这会导致浏览器阻止对下一页/上一页的请求。

我已经仔细检查了初始请求是使用 HTTPS 发出的,而对 this question 的第二个回答指出它应该在形成的 URL 中使用 HTTPS,因为请求是通过 HTTPS 来的。

同一个问题的第一个答案也没有帮助 - 我将 X-Forwarded-Proto 行添加到我的 nginx 配置并重新加载,但无济于事。

The DRF docs 提到 reverse() 应该表现得像基本的 Django 反向,但似乎很清楚初始请求是 HTTPS 而返回的 URL 是 HTTP。

这里有几个显示初始请求的屏幕截图 (https://<domain>.com/api/leaderboard/):

回复包含next: http://<domain>.com/api/leaderboard/?page=2):

我认为这将是一个简单的设置,但在搜索了这个站点和 DRF 站点后都找不到任何东西。

这是我的 nginx 配置:

 location / {
    # proxy_pass http://127.0.0.1:9900;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';

    root /opt/app/client/dist;
    index index.html index.htm;

}

This question 包含一个非常详细的答案,但最终表示 url 是使用与请求相同的协议形成的,这似乎不是这里的情况。我需要设置 Django SECURE_PROXY_SSL_HEADER 吗?考虑到它可能不安全的警告,我不确定。

【问题讨论】:

  • 这个问题我应该给2分。谢谢

标签: django django-rest-framework


【解决方案1】:

我需要设置 Django SECURE_PROXY_SSL_HEADER 吗?考虑到它可能不安全的警告,我不确定。

是的,你知道。 但是,您需要注意自己在做什么。特别要确保它从外部丢弃 X-Forwarded-Proto。

【讨论】:

  • 知道了 - 所以这需要设置 X-Forwarded-Proto 标头和 Django 设置?这部分网络开发/部署对我来说无疑是一个弱点——我如何确保从外部删除 X-Forwarded-Proto?
  • 嗯,我确实添加了这个设置并在我的 nginx 配置中留下了 proxy_set_header 行,但它仍然不起作用。我将继续使用此配置进行测试
  • @prof.phython 我不是,但是使用大部分 url 并自己添加 https 非常简单。我确信有一些方法可以让 DRF 在提供的链接中使用 https,但这个问题中的任何内容都没有帮助我让它工作。
  • @dkhaupt 好的。所以你自己创建了链接?
  • 是的——这个 API 是由 Angular 前端使用的,所以我只是确保在该代码中使用的 url 包含 HTTPS。我没有在后端创建链接。
【解决方案2】:

由于这是谷歌搜索结果中出现的第一个 SO 帖子,我认为分享我如何解决它会很有用。我的具有 Kubernetes 风格,但在底层逻辑与其他逻辑并没有太大区别。

我正在使用位于 Django 前面的 Kubernetes 入口控制器,所以对你来说可能会有所不同,但我会总结一下我的上下文,以便你看看它是否对你有用:

问题

所以我遇到了与 OP 相同的问题,即使我使用 https 访问 api 端点,来自 DRF 的分页链接始终为 http。如果您设置了 Django REST 框架 (DRF) 附带的浏览器 API,您可以更清楚地看到这一点,然后转到 api 根页面。我看到 DRF 生成的所有链接都是http,无论我在访问该站点时使用什么协议。

我的上下文

堆栈,从外部世界到 Django:

  1. Kuberenetes Nginx 入口控制器(使用letsencrypt 设置SSL)
  2. 入口规则指向 Django 服务
  3. Gunicorn 启动 Django 服务器
  4. Django 读取settings.py 并开始服务请求

我如何调试并找出原因

  1. 转储入口控制器的nginx.conf。如果您不知道如何操作:首先通过 kuberctl get pods -n <namespace of your ingress controller> 找出 pod 名称,写下该名称,然后通过 kubectl exec -it -n ingress_controller_namespace ingress_controller_pod_name cat /etc/nginx/nginx.conf > nginx.conf 转储文件。
  2. 查看nginx.conf,检查您的网站域的location 部分中是否有proxy_set_header X-Forwarded-Proto $scheme;,或类似的东西正确设置代理标头X-Forwarded-Proto。默认情况下,Kubernetes nginx 入口控制器应该已经为您提供了这一行(或等效的行)。
  3. 接下来将请求定向到 Gunicorn。一个问题是您需要将--forwarded-allow-ips="*" 添加到您的 gunicorn 命令中,或者如果您知道您的 nginx 服务器 IP,则可以限制为那个,以便 gunicorn 为您转发标头,否则 gunicorn 将剥离这些标头.所以你最终会得到像gunicorn django_server.wsgi:application --forwarded-allow-ips="*" --workers=${PROPER_WORKER_NUM} --log-level info --bind 0.0.0.0:8001 这样的命令。通过指定workers=4,您可以拥有 4 个工作人员,通常取决于您在服务器上的 CPU。 --log-level info 仅用于调试目的,它是可选的。
  4. 在 Django 中,您需要在 settings.py 中有 SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https'),其他答案已经提到。您可能想知道为什么前缀 HTTP_ 而在 nginx 中我们的标题名称是 X-Forwarded-Proto。这是因为 WSGI 会将前缀添加到它识别的标头中。这行基本上说,如果标头 HTTP_X_FORWARDED_PROTO 等于字符串 "https",那么 Django 将请求识别为安全的。这将影响 Django 中的一些行为,例如你会得到request.is_secure == Truerequest.build_absolute_uri(None) == 'https://...',最重要的是,Django REST Framework 分页链接现在将使用https! (只要你确实通过https点击了api)

好的,现在您可以再次测试。如果 DRF 现在给你 https - 恭喜。或者,如果你和我一样,在上面尝试之后,仍然没有运气 - DRF 仍然生成该死的http 链接。我想分享一些我在调试时的技巧:

如果您有DEBUG=True 并且可以访问服务器日志,则通过print() 在Django 中打印出以下值,或者将它们传递给模板上下文并在html 页面中显示它们(如果这样的话)更容易在启用 SSL 的生产环境中进行测试。

  • request.is_secure():如果你没有让 DRF 使用http,你很可能会得到False
  • request.META 为您提供 Django 收到的所有标头。标题HTTP_X_FORWARDED_PROTO 出现了吗?它有什么价值?
    • 我想分享一下,这对我来说是一个令人振奋的时刻:HTTP_X_FORWARDED_PROTO 的值是 https,https,这告诉我它以某种方式具有重复值,我可能将 proxy_set_header 设置了两次,并且答对了!默认情况下,K8 入口控制器已经有了这一行,并且由于我通过 location-snippet 再次添加了它,所以现在我总共有两行 proxy_set_header X-Forwarded-Proto $scheme;。删除我的 sn-p 后,DRF 显示 https,我很高兴能完成这项工作。这不是那么直截了当,因为我认为proxy_set_header 会覆盖并“设置”标题值。但似乎它只是不断附加。

【讨论】:

  • 您好,这很有帮助。但只需更改 proxy_set_header X-Forwarded-Proto $scheme;到 proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;在我的情况下工作。我的设置是客户端 -> http/https -> ALB -> http -> nginx -> http -> django,nginx/django 在同一个 docker 容器中。我假设 $scheme 只是转发传递给 nginx 的协议,而 $http_x_forwarded_proto 包含来自客户端通过 ALB 的原始协议。希望这对其他人有帮助。
  • @Bossam 很高兴您成功了!我认为这是因为在您的情况下,您使用的是 AWS ALB,它将为您设置 $http_x_forwarded_proto。在我的设置中,我使用自己的 nginx 作为负载均衡器,并且我的 nginx 直接暴露于外部流量。我认为这也解释了为什么您的 nginx proxy_set_header X-Forwarded-Proto $scheme 不起作用,因为您的 AWS ALB 可能在内部将所有外部流量引导到 http,所以当您的 nginx 在该 ALB 后面时,您的 $schema 将始终是 http,因此您永远不会在 Django 中获取 https。
  • 看起来确实如此!。感谢您的反馈!
  • 这是一个了不起的答案。对我来说,路线是 NGINX -> Gunicorn -> Django 都在 Docker 实例中运行。在我的 Gunicorn 配置中设置 forwarded_allow_ips = '*' 就可以了。
  • 这个解决方案对我不起作用。我仍然有指向 https 的分页链接。
猜你喜欢
  • 1970-01-01
  • 2016-08-06
  • 2016-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-09
  • 2015-10-22
  • 1970-01-01
相关资源
最近更新 更多