401重定向神奇地导致302重定向。

5
我正在使用Redirect类向未登录用户发送登录页面,使用401状态码:
return Redirect::to('login', 401);

这会发送正确的位置头,但状态码被设为 302
我已经追踪到它最终调用了基础的 Response 类,

laravel/vendor/Symfony/Component/HttpFoundation/Response.php

并且它正在调用:
$this->setStatusCode($status);

使用正确的401代码。


我还尝试了转储该对象:

var_dump( Redirect::to('login', 401)->foundation );

我能看到被保护的statusCode属性已经正确设置为401


然而,生成的响应的HTTP状态码被设置为302

这是怎么回事呢?我使用不当吗?


附言:我也在Laravel论坛上发布了此帖,但没有得到解决。


你在使用什麼操作系统(和服务器)? - LSerni
这个重定向之后还有其他不明显的重定向吗?401 可能有效,但是之后可能会有一个 302 重定向跳进来并进行重定向... 在这里很难说清楚。 - duellsy
4个回答

4

这不是 Laravel 的问题,即使只使用 (php 5.4 in windows),也可以重现此问题:

<?php
header("HTTP/1.1 401 Unauthorized");
header("Location: http://www.google.com");

看起来 PHP 把它设置为 302:

$ php-cgi "test.php"
Status: 302 Moved Temporarily
Location: http://www.google.com
Content-type: text/html; charset=UTF-8

在PHP源代码的main/SAPI.C文件中:
} else if (!STRCASECMP(header_line, "Location")) {
    if ((SG(sapi_headers).http_response_code < 300 ||
        SG(sapi_headers).http_response_code > 307) &&
        SG(sapi_headers).http_response_code != 201) {
        /* Return a Found Redirect if one is not already specified */
        if (http_response_code) { /* user specified redirect code */
            sapi_update_response_code(http_response_code TSRMLS_CC);
        } else if (SG(request_info).proto_num > 1000 && 
           SG(request_info).request_method && 
           strcmp(SG(request_info).request_method, "HEAD") &&
           strcmp(SG(request_info).request_method, "GET")) {
            sapi_update_response_code(303 TSRMLS_CC);
        } else {
            sapi_update_response_code(302 TSRMLS_CC);
        }
    }

正如您所看到的,当您使用“Location”进行header()时,HTTP状态代码将被修改为302。
如果您反过来做,也可以使其正常工作:
<?php
header("Location: http://www.google.com");
header("HTTP/1.1 401 Unauthorized");

这将会产生以下结果:
$ php-cgi "test.php"
Status: 401 Unauthorized
Location: http://www.google.com
Content-type: text/html; charset=UTF-8

但是 Laravel 在设置状态码之后设置位置,因此状态码仍然被设置为 302。但这已经没有意义了,即使您成功地使用位置头将状态码设置为 401,浏览器也不会跟随重定向。


+1 谢谢。有没有其他方法可以解决这个问题?我希望未经授权的用户被重定向到登录页面,但我也需要我的 AJAX 请求知道用户何时已注销(我正在使用类似于 Ben Nadel 在此处概述的方法)。 - Joseph Silber
@JosephSilber jQuery在请求中添加X-Requested-With头,以便让您知道它是使用ajax进行的。然后,您可以为ajax设置401状态,并为传统请求设置重定向状态。 - Esailija
我考虑过这个问题,但曾经在某处读到他们正在计划放弃它,现在找不到来源了。你可以确认一下吗? - Joseph Silber
@JosephSilber在谷歌上搜索了一下,似乎找不到相关信息,但这听起来太疯狂了。所有/大多数ajax库都会添加该标头,因为它非常有用,例如当响应ajax请求时,重定向是没有意义的,因为ajax请求的工作方式非常不同。 - Esailija
值得一提的是,angular.js在一年前就删除了这个标题 - Joseph Silber
显示剩余2条评论

4
浏览器不允许您这样做; 您不能使用401重定向。

1
我一直跟踪它... 直到在 `laravel/vendor/Symfony/Component/HttpFoundation/Response.php` 中的 `public function sendHeaders()`,生成最终状态标头的地方。
   // status
    Header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText));

...然后它还是401。

接着我想起来了。你不能使用401来重定向,这是一个客户端错误。

(这也将不通过同一Response.php文件中指定的isRedirect测试。 mod_php会为您修复它,使其在您发送Location时变成302(因为默认情况下状态为200,所以需要更新。PHP不检查它是否已经更新为其他内容)。


+1 鼓励你在这个问题上深入探究。谢谢。 - Joseph Silber

0
正如其他人所指出的那样,不存在所谓的401重定向。我之所以被卷入这个问题,是因为我需要将非AJAX用户重定向(请参见此链接),但是对于AJAX请求应该返回401状态码

如果你发现自己处于类似的情况中,这是我最终使用的:

return Request::ajax() ?
         Response::make('', 401, array('HTTP/1.1 401 Unauthorized')) :
         Redirect::to('login', 302);

这里使用 Laravel 的方法来处理头部信息。


如果您正在使用原始的PHP,请使用以下代码:
$is_ajax_request = ! empty( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';

$is_ajax_request ?
    header('HTTP/1.1 401 Unauthorized') :
    header('Location: http://site.com/login');

exit;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接