Django REST Framework分页链接不使用HTTPS

时间:2017-03-15 19:25:32

标签: django django-rest-framework

我为某个运行良好的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/)的屏幕截图:

enter image description here

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

enter image description here

我认为这是一个简单的设置,但是在搜索了这个网站和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包含一个非常详细的答案,但最终表示网址是使用与请求相同的协议形成的,这似乎不是这里的情况。我需要设置Django SECURE_PROXY_SSL_HEADER吗?鉴于警告说它可能不安全,我不确定。

2 个答案:

答案 0 :(得分:5)

  

我是否需要设置Django SECURE_PROXY_SSL_HEADER?鉴于警告可能不安全,我不确定。

是的,你这样做。 但是,你需要注意你在做什么。特别要确保它从外面放下X-Forwarded-Proto。

答案 1 :(得分:3)

由于这是google搜索结果中出现的第一篇SO帖子,所以我认为分享我如何解决它会很有用。我的软件具有Kubernetes风格,但在逻辑上并没有什么不同。

我使用的是坐在Django前面的Kubernetes入口控制器,所以情况可能与您有所不同,但是我将总结我的上下文,以便您查看它是否对您有用:

问题

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

我的背景

按从外部世界到Django的顺序排列的堆栈:

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

如何调试并找出原因

  1. 转储入口控制器的nginx.conf。如果您不知道该怎么做:首先用kuberctl get pods -n <namespace of your ingress controller>找出豆荚名称,写下该名称,然后用kubectl exec -it -n ingress_controller_namespace ingress_controller_pod_name cat /etc/nginx/nginx.conf > nginx.conf转储文件。
  2. 查看nginx.conf,检查您的网站域的proxy_set_header X-Forwarded-Proto $scheme;部分中是否有location,或是否正确设置了代理标头X-Forwarded-Proto的类似内容。默认情况下,Kubernetes nginx入口控制器应该已经为您提供了这一行(或等效功能)。
  3. 接下来,将请求定向到Gunicorn。一个陷阱是您想在您的gunicorn命令中添加--forwarded-allow-ips="*",或者如果您知道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中,您需要在SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')中包含settings.py,其他答案也提到了这一点。您可能想知道为什么在nginx中,标题HTTP_是前缀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链接。当我像地狱一样调试时,我想分享一些提示:

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

  • request.is_secure():如果您无法使DRF使用http,则很可能您会获得False
  • request.META提供了Django收到的所有标头。标头HTTP_X_FORWARDED_PROTO是否出现?它有什么价值?
    • 我想分享一下这对我来说是个重要时刻:HTTP_X_FORWARDED_PROTO的值是http,https,这告诉我它以某种方式具有重复的值,并且我可能有{{1 }}设置两次,然后宾果游戏!默认情况下,K8入口控制器已经有这行,并且由于我通过proxy_set_header再次添加了它,所以现在我总共有两行location-snippet。删除代码段后,DRF会显示proxy_set_header X-Forwarded-Proto $scheme;,我非常高兴能够完成此操作。并不是那么简单,因为我认为https将覆盖并“设置”标头值。但似乎只是继续追加。