EventSource 中的 HTTP 授权标头(服务器发送的事件) [英] HTTP Authorization Header in EventSource (Server Sent Events)

查看:104
本文介绍了EventSource 中的 HTTP 授权标头(服务器发送的事件)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要将 Authorization 标头设置为 HTML5 EventSource.自从 Websockets 出现以来,服务器发送的事件似乎已被废弃,我找不到任何有用的文档.我已经找到的方法是在url中传递授权数据......但我不喜欢这种方法.

I need to set an Authorization header to an HTML5 EventSource. As Server Sent Events seems to be disused since Websockets appeared, I cannot find any useful documentation. The approach I have already found is to pass the authorization data within the url... but I don't like this method.

我正在使用 AngularJS 并在 $httpProvider 上设置拦截器,但是 EventSource 没有被 AngularJS 拦截,所以我无法添加任何标题.

I am using AngularJS and set interceptors on $httpProvider, but the EventSource is not intercepted by AngularJS, so I cannot add any header.

推荐答案

我意识到你的帖子是一年多前的了,但我发现自己在同一条船上,现在有了很好的答案.我希望这可以帮助某人,或者至少给他们一些想法......

I realize your post was over a year ago, but I found myself in the same boat with now good answers. I'm hoping this may help someone, or at least give them some ideas...

Cookie 看起来很简单,但是如果有人阻止 Cookie 会发生什么?我必须提示他们启用 cookie 才能使用该站点.那时他们开始怀疑他们是否可以信任该站点,因为他们出于安全原因"禁用了 cookie.一直以来,出于安全原因,我都希望启用 cookie!

Cookies seem easy enough, but what happens if someone is blocking cookies? I would have to prompt them to enable cookies to use the site. At that point they start to wonder if they can trust the site since they disabled cookies for 'security reasons'. All the while, I want cookies enabled for security reasons!

使用 AJAX,人们可以轻松地通过 SSL POST 身份验证数据,但这对于 SSE 来说是不可能的.我看过很多帖子,然后人们说只使用查询字符串",但我不想通过以纯文本(example.com/stream?sessionID=idvalue)发送身份验证数据来损害客户的安全可以窥探.

Using AJAX, one can easily POST authentication data over SSL, but that's just not possible with SSE. I've seen many posts where people then say, "just use the querystring", but I don't want to compromise a customer's security by sending the auth data in plain text (example.com/stream?sessionID=idvalue) which someone could snoop.

绞尽脑汁几个小时后,我意识到我可以在不影响客户身份验证数据的情况下完成总体目标.只是为了澄清,我还没有发现在建立 EventSource 连接时 POST 的某种方法,但它确实允许浏览器在每次重新连接时安全地通过 EventSource 传递身份验证令牌.他们的关键是将所需的 sessionID/token 放入 lastEventID.

After racking my brain for a couple hours I realized that I CAN accomplish the the overall goal without compromising the customer's auth data. Just to clarify, I haven't discovered some way to POST when establishing an EventSource connection, but it does allow the browser to securely pass an authentication token with the EventSource each time it reconnects. They key is to get the desired sessionID/token into the lastEventID.

用户可以像往常一样使用用户名/密码进行身份验证(或通过 AJAX 发布您保存在本地存储中的令牌).AJAX 身份验证过程将传回一个带有短期令牌的 JSON 对象(在 60 秒后到期,或使用时),该令牌将与更持久的令牌一起保存在所需的后端(例如:mySQL)中.此时,您启动 SSE 连接,如:

The user can authenticate as usual with a username/password (or by AJAX POSTing a token you keep in localstorage). The AJAX auth process will pass back a JSON object with a short-lived-token (expires in 60 seconds, or when used) which would be saved in your desired backend (eg: mySQL) along with a longer-lasting token. At this point you initiate your SSE connection like:

    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 中,您将执行以下操作:

In the corresponding PHP you would do something like this:

        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 connects with the short-lived-token, PHP validates against the short-lived-token and deletes it from the DB so it will never be able to AUTH again. This is somewhat similar when you get texted a 6-digit code to login to online banking. We use PHP to push the REAL token (that expires much later) which we retrieved from the database as the event ID. It's not really necessary for Javascript to do anything with this event-- the server will end the connection automatically, but you can listen to the event if you want to do more with it.

此时,自PHP完成脚本以来,SSE连接已经结束.但是,浏览器会自动重新建立连接(通常需要 3 秒).这一次,它将发送 lastEventId... ,我们在断开连接之前将其设置为令牌值.在下一次连接时,此值将用作我们的令牌,应用程序将按预期运行.只要您在发送消息/事件时开始使用真实令牌作为事件 ID,就没有必要断开连接.此令牌值在浏览器接收到它时以及在与服务器的每个后续连接中都通过 SSL 完全加密传输.明文"传输的值在我们收到 & 后的几秒钟内就过期了.使用过它,任何发现它的人都无法再使用它.如果有人尝试使用它,他们将收到 404 RESPONSE.

At this point, the SSE connection has ended since PHP finished the script. However, the browser will automatically reestablish the connection (usually with 3 seconds). This time, it will send the lastEventId... which we set to the token value before we dropped the connection. On the next connection, this value will be used as our token and the app will run as expected. It's not really necessary to drop the connection as long as you start using the real token as the event-ID when you send messages/events. This token value is transmitted completely encrypted over SSL both when the browser receives it, and in every subsequent connection to the server. The value that was transmitted 'in the clear' was expired within seconds from when we receive & used it and it can no longer be used by anyone that discovers it. If someone does attempt to use it they will receive a 404 RESPONSE.

如果您已经将事件流 ID 用于其他目的,这可能无法开箱即用",除非您将 auth-token 和之前使用的值连接起来,并将其拆分为变量,这样它对应用程序的其余部分.类似的东西:

If you already use the event-stream ID for some other purpose, this may not work 'out of the box' unless you concatenate the auth-token and the previously used value, and split it into variables so it's transparent to the rest of the app. Something like:

    // 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]

这篇关于EventSource 中的 HTTP 授权标头(服务器发送的事件)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆