Заголовок авторизации HTTP в EventSource (События, отправленные сервером)
Мне нужно установить заголовок авторизации в EventSource HTML5. Поскольку события, отправленные сервером, кажется, не используются, поскольку появились веб-узлы, я не могу найти какую-либо полезную документацию. Подход, который я уже нашел, заключается в передаче данных авторизации в URL-адресе... но мне этот метод не нравится.
Я использую AngularJS и устанавливаю перехватчики на $httpProvider, но EventSource не перехватывается AngularJS, поэтому я не могу добавить заголовок.
Ответы
Ответ 1
EventSource не имеет API для отправки заголовков HTTP на сервер. Я тоже пытался справиться с этой проблемой, когда создавал в реальном времени чат с использованием SSE.
Однако я думаю, что файлы cookie будут отправляться автоматически, если ваш SSE-сервер является тем же сервером, что и ваш сервер аутентификации.
Ответ 2
Этот polyfill добавляет поддержку заголовка авторизации: https://github.com/Yaffle/EventSource/
Итак, вы можете сделать:
new EventSource("https://domain/stream", { authorizationHeader: "Bearer ..." });
Ответ 3
Я понимаю, что ваш пост был более года назад, но я оказался в одной лодке с хорошими ответами. Я надеюсь, что это может помочь кому-то или хотя бы дать им некоторые идеи...
Файлы cookie кажутся достаточно легкими, но что происходит, если кто-то блокирует файлы cookie? Я бы попросил их включить файлы cookie для использования сайта. В этот момент они начинают задаваться вопросом, могут ли они доверять сайту, так как они отключили куки для "соображений безопасности". Все время я хочу, чтобы файлы cookie включались по соображениям безопасности!
Используя AJAX, можно легко получить данные аутентификации POST через SSL, но это невозможно с помощью SSE. Я видел много сообщений, где люди тогда говорят: "Просто используйте querystring", но я не хочу нарушать безопасность клиента, отправляя данные аутентификации в виде простого текста (example.com/stream?sessionID=idvalue), который кто-то может snoop.
После того, как я провел пару часов в мозгу, я понял, что могу выполнить общую цель без ущерба для данных пользователя клиента. Чтобы уточнить, я не обнаружил способ POST при установлении соединения EventSource, но он позволяет браузеру безопасно передавать токен аутентификации с EventSource каждый раз, когда он повторно соединяется. Ключ их состоит в том, чтобы получить желаемый идентификатор сессии/токена в lastEventID.
Пользователь может аутентифицироваться, как обычно, с помощью имени пользователя/пароля (или AJAX POSTing токена, который вы храните в localstorage). Процесс auth AJAX будет передавать объект JSON с коротким токеном (истекает через 60 секунд или когда он используется), который будет сохранен в вашем желаемом бэкэнд (например, mySQL) вместе с более длинным токеном. На этом этапе вы инициируете свое SSE-соединение, например:
qString = "?slt=" + "value-that-expires-within-seconds";
streamURL = "http://example.com/stream.php";
var streamSource = new EventSource(streamURL + qString);
streamSource.addEventListener('auth',function(e) {
var authStatus = JSON.parse(e.data);
if (authStatus.session !== 'valid') {
qString = "";
streamSource.close();
}
})
В соответствующем PHP вы сделаете что-то вроде этого:
header("Content-Type: text/event-stream\n");
ob_end_flush();
ob_start();
if (isThisShortLivedTokenValid($_GET["slt"])) {
// The short-lived-token is still valid... so we will lookup
// the value of the corresponding longer-lasting token and
// IMMEDIATELY invalidate the short-lived-token in the db.
sendMsg($realToken,'auth','session','valid');
exit;
} else if (isThisRealTokenValid($_SERVER["HTTP_LAST_EVENT_ID"])){
while (1) {
// normal code goes here
// if ($someCondition == 'newDataAvailable') sendMsg($realToken,'chat','msg-id','msg-content');
}
} else {
http_response_code(404); // stop the browser from reconnecting.
exit; //quit the PHP скрипт and don't send anything.
}
function sendMsg($id, $event, $key, $val) {
echo "{" . PHP_EOL;
echo "event: " . $event . PHP_EOL;
echo "id: $id" . PHP_EOL;
echo 'data: {"' . $key . '" : "' . $val . '"}' . PHP_EOL;
echo "}" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
function isThisShortLivedTokenValid($sltValue) {
//stuff to connect to DB and determine if the
//value is still valid for authentication
return $dbResult == $sltValue ? TRUE : FALSE;
}
SSE подключается к короткоживущему токену, PHP проверяет на краткосрочный токен и удаляет его из БД, поэтому он больше не сможет снова использовать AUTH. Это несколько похоже на то, когда вы получаете текстовый код из 6 цифр для входа в онлайн-банкинг. Мы используем PHP для толкания REAL-токена (который истекает намного позже), который мы извлекли из базы данных как идентификатор события. Для Javascript не нужно ничего делать с этим событием - сервер автоматически завершит соединение, но вы можете прослушать это событие, если хотите сделать больше с ним.
На этом этапе соединение SSE закончилось, так как PHP закончил script. Однако браузер автоматически восстановит соединение (обычно с 3 секундами). На этот раз он отправит lastEventId... который мы установили для значения токена, прежде чем мы сбросили соединение. В следующем соединении это значение будет использоваться как наш токен, и приложение будет работать как ожидалось. Не обязательно отключать соединение, пока вы начинаете использовать реальный токен в качестве идентификатора события при отправке сообщений/событий. Это значение токена передается полностью зашифрованным по SSL как при его получении браузером, так и при каждом последующем подключении к серверу. Значение, которое было передано "в ясности", истекло в течение нескольких секунд с момента его получения и использования, и оно больше не может использоваться кем-либо, кто его обнаруживает. Если кто-то попытается использовать его, он получит 404 RESPONSE.
Если вы уже используете идентификатор потока событий для какой-либо другой цели, это может не работать "из коробки", если вы не объедините токен аутентификации и ранее использованное значение и не разделите его на переменные, чтобы он был прозрачным для остальные приложения. Что-то вроде:
// when sending data, send both values
$sseID = $token_value . "_" . $previouslyUsedID;
sendMsg($sseID,'chat','msg-id','msg-content');
// when a new connection is established, break apart the values
$manyIDs = explode("_", $_SERVER["HTTP_LAST_EVENT_ID"])
$token_value = $manyIDs[0]
$previouslyUsedID = $manyIDs[1]
Ответ 4
другой способ передать токен авторизации через URL-адрес в качестве параметра запроса, но вы должны учитывать безопасность. Также добавьте поддержку авторизации через параметр запроса на стороне сервера.
Ответ 5
Если вы используете эту вилку источника событий polyfill, вы сможете добавить заголовки авторизации аналогично тому, как описывает rafaelzlisboa:
https://github.com/AlexGalays/EventSource#923b9a0998fcfd7753040e09aa83764b3cc0230d
Ï не знаю, можете ли вы предоставить заголовок аутентификации в качестве второго аргумента, например, в примере rafaelzlisboa, я получил его для работы, создав объект заголовков и разместив там заголовок авторизации следующим образом:
new EventSource("https://domain/stream", { headers: { Authorization: Bearer.... }});