我需要为HTML5 EventSource设置Authorization标头。由于Websockets出现后服务器发送事件似乎被废弃,我找不到任何有用的文档。我已经找到的方法是在网址中传递授权数据......但我不喜欢这种方法。
我正在使用AngularJS并在$ httpProvider上设置拦截器,但是AngularJS没有截获EventSource,所以我无法添加任何头。
答案 0 :(得分:9)
EventSource没有API用于向服务器发送HTTP标头。当我使用SSE构建实时聊天时,我也在努力解决这个问题。
但是,如果您的SSE服务器与您的身份验证服务器是同一台服务器,我认为会自动发送cookie。
答案 1 :(得分:8)
我意识到你的帖子已经超过一年了,但我发现自己在同一条船上,现在有了很好的答案。我希望这可以帮助某人,或者至少给他们一些想法......
Cookie似乎很容易,但如果有人阻止Cookie会发生什么?我必须提示他们启用cookie才能使用该网站。在那时,他们开始怀疑他们是否可以信任该网站,因为他们因为安全原因而禁用了cookie#39;一直以来,出于安全考虑,我希望启用cookie!
使用AJAX,可以轻松地通过SSL发布身份验证数据,但这对SSE来说是不可能的。我看过许多帖子,然后人们说,"只是使用查询字符串",但我不想通过以纯文本形式发送auth数据来危及客户的安全性(example.com/stream?sessionID=idvalue)有人可以窥探。
经过几个小时的努力,我意识到我可以在不影响客户身份验证数据的情况下实现总体目标。为了澄清一下,我在建立EventSource连接时没有发现一些POST方法,但它确实允许浏览器在每次重新连接时安全地传递一个带有EventSource的身份验证令牌。它们的关键是将所需的sessionID /令牌放入lastEventID。
用户可以像往常一样使用用户名/密码进行身份验证(或通过AJAX POSTing您保存在localstorage中的令牌)。 AJAX身份验证过程将使用短期令牌(在60秒内过期,或在使用时)传回JSON对象,该对象将保存在您所需的后端(例如: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 script 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令牌(稍后会过期)作为事件ID。 Javascript对此事件没有必要做任何事情 - 服务器会自动结束连接,但如果你想用它做更多的事情,你可以听取这个事件。
此时,自PHP完成脚本以来,SSE连接已经结束。但是,浏览器将自动重新建立连接(通常为3秒)。这次,它将发送lastEventId ...我们在删除连接之前设置为令牌值。在下一个连接上,此值将用作我们的令牌,应用程序将按预期运行。只要您在发送消息/事件时开始使用真实令牌作为事件ID,就不必断开连接。此标记值在浏览器接收时以及在与服务器的每个后续连接中通过SSL完全加密传输。在明确的'中传输的价值。从我们收到&使用它,任何发现它的人都不能再使用它了。如果有人尝试使用它,他们将收到 404回复。
如果您已将事件流ID用于其他目的,则可能无法使用'开箱即用'除非您连接auth-token和以前使用的值,并将其拆分为变量,以便它对应用程序的其余部分透明。类似的东西:
// 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]
答案 2 :(得分:7)
此polyfill添加了授权标头支持:https://github.com/Yaffle/EventSource/
所以你可以这样做:
new EventSource("https://domain/stream", { authorizationHeader: "Bearer ..." });
答案 3 :(得分:3)
传递身份验证令牌的另一种方法是通过URL作为查询参数,但您应该考虑安全性。还可以通过服务器端的查询参数添加对授权的支持。
答案 4 :(得分:2)
如果您使用事件源polyfill的这个分支,您将能够像rafaelzlisboa描述的方式一样添加授权标头: https://github.com/AlexGalays/EventSource#923b9a0998fcfd7753040e09aa83764b3cc0230d
我不知道你是否可以提供身份验证标头作为第二个参数,就像在rafaelzlisboa的例子中一样,我通过创建一个头文件对象并将我的授权标题放在那里来实现它,如下所示:
new EventSource("https://domain/stream", { headers: { Authorization: Bearer.... }});
答案 5 :(得分:1)
window.EventSource
似乎还不支持传递其他标头。好消息是EventSource
的其他一些实现也支持其他标头。其中一些如下:
const eventSource = new EventSource(resoureUrl, {
headers: {
'Authorization': 'Bearer ' + authorizationToken;
}
});
es.onmessage = result => {
const data = JSON.parse(result.data);
console.log('Data: ', data);
};
es.onerror = err => {
console.log('EventSource error: ', err);
};
答案 6 :(得分:0)
我仔细阅读了几篇文章,以试图确定auth令牌是否在EventSource()调用中发送。尽管有polyfill选项可以添加标题:https://github.com/whatwg/html/issues/2177 而其他人提到通过ssl发送身份验证令牌。
与其使用polyfill EventSource()或通过ssl在查询参数中发送auth令牌,不如通过ssl在EventSource url的参数中发送eventSource标识符(eventSrcUUID):-
在用户身份验证时,eventSrcUUID随服务器上的sseEmitter一起生成,并放置在sseEmitterMap中。
客户端从响应中检索eventSrcUUID,并使用参数中的eventSrcUUID调用EventSource()调用。在服务器上,引用sseEmitterMap来检索eventSrc对象。会话数据中保存的sseEmitter对象用于向客户端发送事件通知。
答案 7 :(得分:0)
我通过在sse调用之前进行额外的休息解决了此问题,这种休息是普通的休息,将需要与SSE调用和get以及OTP作为响应所需要的安全协议相同的安全协议。 并发送此OTP以查看呼叫查询参数,并在Web过滤器中验证此OTP并将其替换为身份验证标头。