Django @login_required 使用 nginx 和 fastcgi 时导致重定向循环

Posted

技术标签:

【中文标题】Django @login_required 使用 nginx 和 fastcgi 时导致重定向循环【英文标题】:Django @login_required cause redirects loop when using nginx and fastcgi 【发布时间】:2013-02-14 11:56:13 【问题描述】:

我在 settings.py 中这样设置 LOGIN_URL:

LOGIN_URL = '/login/'

在 urls.py 我和这个 URLConf:

(r'^$', 'agent.index.redirect'),
(r'^login/$', 'django.contrib.auth.views.login', 'template_name':'login.html'),

agent.index.redirect 视图是这样的:

@login_required
def redirect(request):
    ...

我像这样运行我的 Django 网站:

python manage.py runfcgi host=127.0.0.1 port=8090 --settings=settings

nginx.conf 是这样的:

user nobody nobody;
worker_processes  5;
#error_log /var/log/nginx/error_log info;

events 
    worker_connections  1024;
    use epoll;


http 
    include         mime.types;
    default_type    application/octet-stream;

    log_format main
        '$remote_addr - $remote_user [$time_local] '
        '"$request" $status $bytes_sent '
        '"$http_referer" "$http_user_agent" '
        '"$gzip_ratio"';

    client_header_timeout   10m;
    client_body_timeout     10m;
    send_timeout            10m;

    connection_pool_size            256;
    client_header_buffer_size       1k;
    large_client_header_buffers     4 2k;
    request_pool_size               4k;

    gzip on;
    gzip_min_length 1100;
    gzip_buffers    4 8k;
    gzip_types      text/plain;

    output_buffers  1 32k;
    postpone_output 1460;

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;

    keepalive_timeout       75 20;

    ignore_invalid_headers  on;
    index index.html;

    server 
        listen 80;
        server_name localhost;
        root /web/agent;

        location /static  
            alias /web/agent/media;
            access_log   off;
            expires      30d;
        

        location /media  
            alias /web/agent/admin_media;
            access_log   off;
            expires      30d;
        

        location / 
            # host and port to fastcgi server
            fastcgi_pass 127.0.0.1:8090;
            fastcgi_param PATH_INFO $fastcgi_script_name;
            fastcgi_param REQUEST_METHOD $request_method;
            fastcgi_param QUERY_STRING $query_string;
            fastcgi_param CONTENT_TYPE $content_type;
            fastcgi_param CONTENT_LENGTH $content_length;
            fastcgi_pass_header Authorization;
            fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
            fastcgi_param  REQUEST_URI        $request_uri;
            fastcgi_param  DOCUMENT_URI       $document_uri;
            fastcgi_param  DOCUMENT_ROOT      $document_root;
            fastcgi_param  SERVER_PROTOCOL    $server_protocol;
            fastcgi_param  HTTPS              $https if_not_empty;
            fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
            fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
            fastcgi_param  REMOTE_ADDR        $remote_addr;
            fastcgi_param  REMOTE_PORT        $remote_port;
            fastcgi_param  SERVER_ADDR        $server_addr;
            fastcgi_param  SERVER_PORT        $server_port;
            fastcgi_param  SERVER_NAME        $server_name;
            fastcgi_connect_timeout 30s;
            fastcgi_send_timeout 30s;
            fastcgi_read_timeout 30s;
            fastcgi_buffer_size 128k;
            fastcgi_buffers 8 128k;#8 128
            fastcgi_busy_buffers_size 256k;
            fastcgi_temp_file_write_size 256k;
            fastcgi_intercept_errors on;
        
        #access_log     /usr/local/nginx/logs/access_log main;
        #error_log      /usr/local/nginx/logs/error_log;
    

当我访问 [http://localhost] 时,会有一个重定向循环。而nginx的access.log是这样的:

127.0.0.1 - - [28/Feb/2013:18:13:51 +0800] "GET / HTTP/1.1" 302 5 "-" "Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20130220 Firefox/17.0"
127.0.0.1 - - [28/Feb/2013:18:13:51 +0800] "GET /login/?next=// HTTP/1.1" 302 5 "-" "Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20130220 Firefox/17.0"
127.0.0.1 - - [28/Feb/2013:18:13:51 +0800] "GET /login/?next=/login//%3Fnext%3D// HTTP/1.1" 302 5 "-" "Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20130220 Firefox/17.0"
127.0.0.1 - - [28/Feb/2013:18:13:51 +0800] "GET /login/?next=/login//%3Fnext%3D/login//%253Fnext%253D// HTTP/1.1" 302 5 "-" "Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20130220 Firefox/17.0"
127.0.0.1 - - [28/Feb/2013:18:13:51 +0800] "GET /login/?next=/login//%3Fnext%3D/login//%253Fnext%253D/login//%25253Fnext%25253D// HTTP/1.1" 302 5 "-" "Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20130220 Firefox/17.0"
...and so on.

谁能帮忙解决这个问题?

【问题讨论】:

你的问题让我想起了this question。尝试删除fastcgi_param SCRIPT_NAME $fastcgi_script_name; @Alasdair 它有效。非常感谢!但我不知道为什么。 我很高兴它成功了 :) 【参考方案1】:

啊哈,原因是:

Django 使用 PATH_INFO 来匹配 urlpatterns。 Nginx 的 fastcgi_params include 没有设置。它确实设置了 SCRIPT_NAME。如果 PATH_INFO 和 SCRIPT_NAME 都设置为 $fastcgi_script_name,Django 似乎会为所有请求获取一个空路径。只需设置 PATH_INFO!

在http://aftnn.org/2009/jan/23/nginx-django-fastcgi/ 上查看更多信息。

所以解决方案只是删除这一行:fastcgi_param SCRIPT_NAME $fastcgi_script_name; 非常感谢@Alasdair。

【讨论】:

【参考方案2】:

这通常表明您有另一个与请求的 URL(可能是所有 url)匹配并重定向到登录页面的 URL 模式。例如:

(r'^', 'agent.index.redirect'),
    ^ left out the end-of-string $

这会导致您遇到的重定向循环。除了问题中列出的以外,您还有其他 url 格式吗?

【讨论】:

只有在我使用nginx和fastcgi时才会出现这个问题。如果使用python manage.py runserver 运行站点,则没有重定向循环。

以上是关于Django @login_required 使用 nginx 和 fastcgi 时导致重定向循环的主要内容,如果未能解决你的问题,请参考以下文章

Django @login_required 删除 https

使用Django自带的登录访问限制login_required

为啥Django在使用@login_required装饰器时有太多的登录重定向?

Django中 @login_required用法简介

Django 中的 login_required 装饰器和 urlresolver.reverse()

Django:使用@login_required和设置LOGIN_URL时的信息泄露问题