Подтвердить что ты не робот

Можно ли отправить 401 Unauthorized AND redirect (с местоположением)?

Я хочу отправить 401 Unauthorized и перенаправить клиент где-нибудь. Однако:

если я сделаю это следующим образом:

header('HTTP/1.1 401 Unauthorized');
header('Location: /');

сервер отправляет 302 Found с Location, поэтому не 401 Unauthorized.

Если я сделаю это так:

header('Location: /');
header('HTTP/1.1 401 Unauthorized');

браузер получает как 401 Unauthorized, так и Location, но не перенаправляет.

(IE 9 и Chrome 16 ведут себя одинаково, поэтому я предполагаю, что это правильно)

Возможно, я злоупотребляю HTTP? Я бы хотел, чтобы мой интерфейс приложения был абсолютно одинаковым для всех клиентов: текстовый браузер, современный браузер, вызовы API и т.д. Текст ответа 401 + сказал бы пользователю API о том, что. Переадресация полезна для браузера.

Есть ли (хороший) способ?

4b9b3361

Ответ 1

По определению (см. RFC 2162), код ответа HTTP 302 - код перенаправления. Без него заголовок местоположения может быть проигнорирован.

Однако вы можете отправить ответ HTTP 401 и отобразить вывод. Вместо того, чтобы перенаправлять пользователя на страницу с ошибкой, вы можете просто написать свой контент, который хотите отправить в тело HTTP, в том же запросе.

Ответ 2

3xx означает перенаправление
4xx означает, что браузер сделал что-то неправильно.

Есть причина, по которой коды разделены так, как они есть, - они не смешиваются;)

Ответ 3

Я прихожу сюда очень поздно, но я думал, что добавлю свои два цента. Насколько я понимаю, желание состоит в том, чтобы указать, что пользователь не имеет правильной авторизации и предлагает им войти в систему. Руди понятно хотел бы вернуть 401 Несанкционированный (поскольку пользователю необходимо авторизовать каким-то механизмом, например, протоколирование в), а также пересылать их на страницу входа в систему, но это не очень легко выполнить и не поддерживается в большинстве библиотек. Одним из решений является отображение страницы входа в тело ответа 401, как было предложено в другом ответе. Однако позвольте мне взглянуть на это с точки зрения сложившейся/лучшей практики.

Тестовый пример 1: Facebook

Переход на защищенную страницу Facebook (мой профиль пользователя) при выходе из системы приводит к ответу 404 Not Found. Facebook предоставляет общую страницу "Эта страница недоступна", которая также включает в себя регистрационную форму. Интересно. Еще более интересно: когда я перехожу к странице "События", я обслуживаю ответ 302, который пересылается на страницу входа (которая возвращает ответ 200). Поэтому я предполагаю, что их идея состоит в том, чтобы вернуть 302 для страниц, которые, как мы знаем, существуют, но служат 404 для страниц, которые могут или не могут существовать (например, для защиты конфиденциальности пользователей).

Тестовый пример 2: Входящие Google

Переход на мой почтовый ящик при выходе из системы возвращает 302 и пересылает меня на страницу входа, похожую на Facebook. Мне не удалось выяснить, как сделать мой профиль Google+ приватным, поэтому нет тестовых данных...

Тестовый пример 3: Amazon.com

Переход к моей истории заказов, когда я вышел из системы, возвращает 302 и пересылает меня на страницу входа, как и раньше. У Amazon нет понятия страницы "профиля", поэтому я тоже не могу проверить это.

Чтобы обобщить здесь тестовые примеры, лучше всего вернуть 302 Найдено и перейти на страницу входа в систему, если пользователь должен войти в систему (хотя я бы утвердил, что 303 См. "Другое" на самом деле более уместно). Это, конечно, только в том случае, когда реальный пользователь должен вводить имя пользователя и пароль в форме html. Для других типов аутентификации (например, базовый, ключ api и т.д.) 401 Несанкционированный, очевидно, является подходящим ответом. В этом случае нет необходимости пересылать страницу входа.

Ответ 4

В дополнение к тонким ответам от Kolink и David (+ 1's), я хотел бы указать, что вы пытаетесь изменить семантику протокола HTTP, возвращая 401 и указав браузер на перенаправление. Это не то, как должен работать HTTP-протокол, и если вы найдете способ получить этот результат, клиенты HTTP найдут поведение вашего сервиса нестандартным.

Либо вы отправляете 401 и разрешаете браузеру работать с ним, либо обрабатываете ситуацию по-разному (например, как предлагается один из комментаторов, перенаправлять на страницу входа или, возможно, на страницу, объясняющую, почему у пользователя нет доступа).

Ответ 5

Вы можете отправить 401, а затем в тело ответа вы можете отправить window.location = 'domain.com'. Однако пользователь будет немедленно перенаправлен, не зная, что произошло 401.

Ответ 6

Вот чистый способ:

На странице 401 вы можете выбрать "представление" для отправки на основе заголовка "accept" в запросе.

Если accept - application/json, вы можете включить тело:

{"status":401;"message":"Authentication required"}

Если "accept" равен text/html, вы можете включить тело:

<form action="/signin" method="post">
    <!-- bla bla -->
    <input type="hidden" name="redirect" value="[URL ENCODE REQUEST URI]">
</form>

Затем вы столкнетесь с тем же вопросом... вы делаете 200 OK или 302 Found при успешном входе в систему? (см., что я там сделал?)

Если вы можете обрабатывать аутентификацию на любой странице, вы можете просто указать, что действие формы - это один и тот же URL-адрес страницы, но смотреть XSS, если вы помещаете пользователя request_uri в атрибут action.

Ответ 7

Веб-браузеры не являются клиентами REST. Придерживайтесь статуса отправки 200 с заголовком местоположения и без содержимого тела. 30-кратные переадресации предназначены для перемещенных страниц. Ни один другой код статуса/заголовок местоположения не следует перенаправлять в веб-браузере.

В качестве альтернативы ваш веб-сервер может иметь настраиваемые страницы ошибок. Вы можете добавить javascript на страницу с ошибкой для перенаправления.

Ответ 8

Может ли это быть тем, что вам нужно? Пришел к этому сам по себе с тем же вопросом.

if(user_has_no_rights()) 
{ 
   header('HTTP/1.1 401 Unauthorized');
   echo $this->requestAction('anothercontroller/action', 
array('return')); 

   $this->autoRender = false;
   $this->layout = ''; 
   die(); 
}