作者:好kc好先生之家 | 来源:互联网 | 2023-05-24 18:29
我需要为HTML5 EventSource设置一个Authorization标头.由于Websockets出现后服务器发送事件似乎被废弃,我找不到任何有用的文档.我已经发现的方法是在URL中传递授权数据......但我不喜欢这种方法.
我正在使用AngularJS并在$ httpProvider上设置拦截器,但是AngularJS没有拦截EventSource,所以我无法添加任何头.
1> 小智..:
此polyfill添加了Authorization Header支持:https://github.com/Yaffle/EventSource/
所以你可以这样做:
new EventSource("https://domain/stream", { authorizationHeader: "Bearer ..." });
关于此实现的注释:仔细阅读代码后,看起来它包装了带有计时器的XMLHttpRequest以完成类似EventSource的结果。不要批评,只是权衡服务器上的连接负载时要考虑的一点。
它不是polyfill,因为本机EventSource不支持用户定义的标头,因此当您的代码将使用本机EventSource时,您仅会得到隐藏的错误/错误。
2> srigi..:
EventSource没有用于将HTTP标头发送到服务器的API.当我使用SSE构建实时聊天时,我也在努力解决这个问题.
但是,如果您的SSE服务器与您的身份验证服务器是同一台服务器,我认为会自动发送COOKIE.
3> 小智..:
我意识到你的帖子已经超过一年了,但我发现自己在同一条船上,现在有了很好的答案.我希望这可以帮助某人,或者至少给他们一些想法......
COOKIEs似乎很容易,但如果有人阻止COOKIE会发生什么?我必须提示他们启用COOKIE才能使用该网站.在那时,他们开始怀疑他们是否可以信任该网站,因为他们因"安全原因"禁用了COOKIE.一直以来,出于安全考虑,我希望启用COOKIE!
使用AJAX,可以轻松地通过SSL POST验证数据,但这对SSE来说是不可能的.我看过许多帖子,然后人们说"只是使用查询字符串",但我不想通过以纯文本(example.com/stream?sessiOnID=idvalue)发送auth数据来破坏客户的安全性可以窥探.
在绞尽脑汁几个小时后,我意识到我可以在不影响客户身份验证数据的情况下实现整体目标.为了澄清,我在建立EventSource连接时没有发现一些POST方法,但它确实允许浏览器在每次重新连接时安全地传递带有EventSource的身份验证令牌.它们的关键是将所需的sessionID/token添加到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]