Можно ли отправить 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 о том, что. Переадресация полезна для браузера.
Есть ли (хороший) способ?
Ответы
Ответ 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();
}