簡體   English   中英

EventSource 中的 HTTP 授權標頭(服務器發送的事件)

[英]HTTP Authorization Header in EventSource (Server Sent Events)

我需要將 Authorization 標頭設置為 HTML5 EventSource。 自從 Websockets 出現以來,服務器發送的事件似乎已被廢棄,我找不到任何有用的文檔。 我已經找到的方法是在 url 中傳遞授權數據......但我不喜歡這種方法。

我正在使用 AngularJS 並在 $httpProvider 上設置攔截器,但是 AngularJS 沒有攔截 EventSource,因此我無法添加任何標頭。

我意識到你的帖子是一年多前的,但我發現自己在同一條船上,現在有了很好的答案。 我希望這可以幫助某人,或者至少給他們一些想法......

Cookie 看起來很簡單,但是如果有人阻止 Cookie 會發生什么? 我必須提示他們啟用 cookie 才能使用該站點。 那時他們開始懷疑他們是否可以信任該站點,因為他們出於“安全原因”禁用了 cookie。 一直以來,出於安全原因,我都希望啟用 cookie!

使用 AJAX,人們可以輕松地通過 SSL POST 身份驗證數據,但這對於 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 針對短期令牌進行驗證並將其從數據庫中刪除,因此它將永遠無法再次進行 AUTH。 當您收到一個 6 位數的代碼以登錄網上銀行時,這有點類似。 我們使用 PHP 來推送我們從數據庫中作為事件 ID 檢索到的 REAL 令牌(過期很久)。 Javascript 沒有必要對這個事件做任何事情——服務器會自動結束連接,但如果你想用它做更多事情,你可以收聽這個事件。

此時,自 PHP 完成腳本以來,SSE 連接已經結束。 但是,瀏覽器會自動重新建立連接(通常需要 3 秒)。 這一次,它將發送 lastEventId... 我們在斷開連接之前將其設置為令牌值。 在下一次連接時,此值將用作我們的令牌,應用程序將按預期運行。 只要您在發送消息/事件時開始使用真實令牌作為事件 ID,就沒有必要斷開連接。 此令牌值在瀏覽器接收到它時以及在與服務器的每個后續連接中都通過 SSL 完全加密傳輸。 “明文”傳輸的值在我們收到並使用它后幾秒鍾內就過期了,任何發現它的人都不能再使用它。 如果有人嘗試使用它,他們將收到404 RESPONSE

如果您已經將事件流 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]

EventSource 沒有用於將 HTTP 標頭發送到服務器的API 當我使用 SSE 構建實時聊天時,我也遇到了這個問題。

但是,如果您的 SSE 服務器與您的身份驗證服務器是同一台服務器,我認為 cookie 將自動發送。

這個 polyfill 添加了 Authorization Header 支持: https : //github.com/Yaffle/EventSource/

所以你可以這樣做:

new EventSource("https://domain/stream", { authorizationHeader: "Bearer ..." });

window.EventSource似乎還不支持傳遞額外的標頭。 好消息是還有一些其他流行的EventSource實現支持額外的標頭。 其中一些如下:

const eventSource = new EventSource(resoureUrl, {
            headers: {
                'Authorization': 'Bearer ' + authorizationToken
            }
        });

eventSource.onmessage = result => {
    const data = JSON.parse(result.data);
    console.log('Data: ', data);
};

eventSource.onerror = err => {
    console.log('EventSource error: ', err);
};

如果您使用事件源 polyfill 的這個分支,您將能夠以類似於 rafaelzlisboa 描述的方式添加授權標頭: https : //github.com/AlexGalays/EventSource#923b9a0998fcfd7753040e09aa83764b3cc0230d

Ï 不知道您是否可以像在 rafaelzlisboa 的示例中一樣提供身份驗證標頭作為第二個參數,我通過創建標頭對象並將我的授權標頭放入其中來使其工作,如下所示:

new EventSource("https://domain/stream", { headers: { Authorization: Bearer.... }});

傳遞身份驗證令牌的另一種方法是通過 URL 作為查詢參數,但您應該考慮安全性。 還通過服務器端的查詢參數添加授權支持。

我在 sse 調用之前通過額外的休息解決了這個問題,這個休息是普通的休息,需要與我們在 SSE 調用和 get 和 OTP 響應中所需的相同的安全協議。 並發送此 OTP 以查看呼叫查詢參數並在 Web 過濾器中驗證此 OTP 並將其替換為身份驗證標頭。

我瀏覽了很多帖子,試圖弄清楚是否在 EventSource() 調用中發送了身份驗證令牌。 盡管有允許添加標頭的 polyfill 替代方案: https : //github.com/whatwg/html/issues/2177而其他人提到通過 ssl 發送身份驗證令牌。

不是使用 polyfill EventSource() 或通過 ssl 在查詢參數中發送身份驗證令牌,而是通過 ssl 在 EventSource url 的參數中發送一個 eventSource 標識符(eventSrcUUID),如下所示:-

在用戶身份驗證時,eventSrcUUID 與服務器上的 sseEmitter 一起生成並放置在 sseEmitterMap 中。

客戶端從響應中檢索 eventSrcUUID 並使用參數中的 eventSrcUUID 調用 EventSource() 調用。 在服務器上,引用 sseEmitterMap 來檢索 eventSrc 對象。 保存在會話數據中的 sseEmitter 對象用於向客戶端發送事件通知。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM