我知道你的帖子是一年多以前的事了,但我发现自己在同一条船上,现在有了很好的答案。我希望这可以帮助某人,或者至少给他们一些想法......
Cookie 看起来很简单,但如果有人阻止了 Cookie,会发生什么?我必须提示他们启用 cookie 才能使用该网站。那时他们开始怀疑他们是否可以信任该网站,因为他们出于“安全原因”禁用了 cookie。一直以来,出于安全原因,我希望启用 cookie!
使用 AJAX,可以轻松地通过 SSL 发布身份验证数据,但使用 SSE 则无法做到这一点。我看过很多帖子,人们说“只使用查询字符串”,但我不想通过以纯文本 (example.com/stream?sessionID=idvalue) 发送身份验证数据来危害客户的安全可以窥探。
绞尽脑汁几个小时后,我意识到我可以在不损害客户身份验证数据的情况下实现总体目标。澄清一下,在建立 EventSource 连接时,我还没有发现某种 POST 方法,但它确实允许浏览器在每次重新连接时安全地通过 EventSource 传递身份验证令牌。他们的关键是将所需的 sessionID/token 放入 lastEventID。
用户可以像往常一样使用用户名/密码进行身份验证(或通过 AJAX 发布您保存在本地存储中的令牌)。 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 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 验证短期令牌并将其从数据库中删除,因此它将永远无法再次进行身份验证。当您收到一个 6 位数的代码以登录网上银行时,这有点相似。我们使用 PHP 推送我们从数据库中检索的作为事件 ID 的 REAL 令牌(过期很久)。 Javascript 没有必要对这个事件做任何事情——服务器会自动结束连接,但是如果你想用它做更多的事情,你可以监听这个事件。
此时,SSE 连接已经结束,因为 PHP 完成了脚本。但是,浏览器会自动重新建立连接(通常需要 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]