javascript - EventSource(服务器发送的事件)中的 HTTP 授权 header

标签 javascript angularjs html http websocket

我需要为 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/

相关文章:

javascript - 为什么我尝试在 AngularJs 中获取非对象错误的属性?

jquery - 在我的链接函数中注册的事件监听器未被触发

html - 如何让视频有 Angular

javascript - 在 CoffeeScript 中,我可以像在 JavaScript 中那样在声明之上实例化一个类吗?

javascript - jQuery 优化代码模式

javascript - 否则如果在 JavaScript 中

javascript - 在 jQuery 中使用 next() x 次

javascript - 如何缓存同一用户重复调用的 AngularJS 指令?

javascript - 使用 show() 显示 div 时显示方法

html - 具有 3 个固定宽度列的页面