我需要为 HTML5 EventSource 设置授权 header 。由于 Websockets 出现后 Server Sent Events 似乎被废弃了,我找不到任何有用的文档。我已经找到的方法是在 url 中传递授权数据...但我不喜欢这种方法。
我正在使用 AngularJS 并在 $httpProvider 上设置了拦截器,但是 EventSource 没有被 AngularJS 拦截,所以我无法添加任何 header 。
最佳答案
我知道你的帖子是一年多以前的,但我发现自己在同一条船上,现在得到了很好的答案。我希望这可以帮助某人,或者至少给他们一些想法...
Cookie 看起来很简单,但是如果有人阻止了 cookie 会怎样?我必须提示他们启用 cookie 才能使用该网站。那时他们开始怀疑他们是否可以信任该站点,因为他们出于“安全原因”禁用了 cookie。出于安全原因,我一直希望启用 cookie!
使用 AJAX,可以轻松地通过 SSL POST 身份验证数据,但使用 SSE 是不可能的。我看过很多帖子,然后人们说“只需使用查询字符串”,但我不想通过以纯文本形式发送身份验证数据 (example.com/stream?sessionID=idvalue) 来损害客户的安全可以窥探。
在绞尽脑汁几个小时后,我意识到我可以在不损害客户的授权数据的情况下实现总体目标。澄清一下,在建立 EventSource 连接时,我还没有发现某种 POST 方法,但它确实允许浏览器在每次重新连接时安全地通过 EventSource 传递身份验证 token 。他们的关键是将所需的 sessionID/token 放入 lastEventID。
用户可以像往常一样使用用户名/密码进行身份验证(或通过 AJAX 发布您保存在本地存储中的 token )。 AJAX 身份验证过程将传回一个带有短期 token (在 60 秒后或使用时过期)的 JSON 对象,该 token 将与一个更持久的 token 一起保存在您想要的后端(例如: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 与短期 token 连接,PHP 验证短期 token 并将其从数据库中删除,因此它永远无法再次进行 AUTH。当您收到用于登录网上银行的 6 位代码时,这有点类似。我们使用 PHP 推送我们从数据库中检索到的 REAL token (过期时间很晚)作为事件 ID。 Javascript 没有必要对这个事件做任何事情——服务器会自动结束连接,但是如果你想用它做更多的事情,你可以监听这个事件。
此时,SSE 连接已经结束,因为 PHP 完成了脚本。但是,浏览器会自动重新建立连接(通常需要 3 秒)。这一次,它将发送 lastEventId... 我们在断开连接之前将其设置为 token 值。在下一次连接时,这个值将用作我们的 token ,应用程序将按预期运行。只要您在发送消息/事件时开始使用真实 token 作为事件 ID,就没有必要断开连接。此 token 值在浏览器接收时以及在与服务器的每个后续连接中都通过 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]
关于javascript - EventSource(服务器发送的事件)中的 HTTP 授权 header ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28176933/