wake-up-neo.com

HTTP-Autorisierungsheader in EventSource (Server Sent Events)

Ich muss einen Authorization-Header auf eine HTML5-EventSource setzen. Da Server Sent Events seit Erscheinen von Websockets scheinbar nicht mehr verwendet wird, kann ich keine nützliche Dokumentation finden. Die Methode, die ich bereits gefunden habe, besteht darin, die Autorisierungsdaten innerhalb der URL zu übergeben ... aber diese Methode gefällt mir nicht.

Ich verwende AngularJS und setze Interceptors für $ httpProvider, aber die EventSource wird nicht von AngularJS abgefangen. Daher kann ich keinen Header hinzufügen.

EventSource hat kein API zum Senden von HTTP-Headern an den Server. Ich hatte auch mit diesem Problem zu kämpfen, als ich Echtzeit-Chat mit SSE erstellte.

Ich denke jedoch, dass Cookies automatisch gesendet werden, wenn Ihr SSE -Server derselbe Server ist wie Ihr Authentifizierungsserver.

9
srigi

Ich weiß, dass Ihr Beitrag vor über einem Jahr war, aber ich befand mich im selben Boot mit jetzt guten Antworten. Ich hoffe, dass dies jemandem helfen kann oder ihnen zumindest ein paar Ideen gibt ...

Cookies scheinen einfach zu sein, aber was passiert, wenn jemand Cookies blockiert? Ich müsste sie auffordern, Cookies zur Verwendung der Website zu aktivieren. Zu diesem Zeitpunkt fragen sie sich, ob sie der Site vertrauen können, da sie Cookies aus Sicherheitsgründen deaktiviert haben. Ich möchte, dass Cookies aus Sicherheitsgründen aktiviert werden!

Mit AJAX kann man leicht POST Daten über SSL authentifizieren, aber mit SSE ist das einfach nicht möglich. Ich habe viele Posts gesehen, in denen die Leute dann sagen "Verwenden Sie einfach den Querstring", aber ich möchte die Sicherheit eines Kunden nicht gefährden, indem Sie die Auth-Daten in Klartext (example.com/stream?sessionID=idvalue) an jemanden senden könnte schnüffeln.

Nachdem ich mir ein paar Stunden den Kopf zerbrochen hatte, wurde mir klar, dass ich das Gesamtziel erreichen kann, ohne die Authentifizierungsdaten des Kunden zu beeinträchtigen. Nur um das zu klären, habe ich beim Aufbau einer EventSource-Verbindung keine Möglichkeit gefunden, POST, aber der Browser kann bei jeder erneuten Verbindung ein Authentifizierungs-Token sicher mit der EventSource übergeben. Der Schlüssel ist, die gewünschte sessionID/das Token in die lastEventID einzugeben.

Der Benutzer kann sich wie üblich mit einem Benutzernamen/Kennwort authentifizieren (oder mit AJAX POST eines Token, das Sie im localstorage aufbewahren). Der AJAX - Authentifizierungsprozess gibt ein JSON-Objekt mit einem kurzlebigen Token zurück (läuft in 60 Sekunden ab oder wird verwendet), das in Ihrem gewünschten Backend (z. B. mySQL) zusammen mit einer längeren Lebensdauer gespeichert würde Zeichen. An diesem Punkt initiieren Sie Ihre SSE -Verbindung wie folgt:

    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();
        }
    })

In dem entsprechenden PHP würden Sie so etwas tun:

        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 stellt eine Verbindung mit dem kurzlebigen Token her, PHP validiert das kurzlebige Token und löscht es aus der Datenbank, sodass es nie wieder AUTH möglich ist. Dies ist etwas ähnlich, wenn Sie einen 6-stelligen Code erhalten, um sich beim Online-Banking anzumelden. Wir verwenden PHP, um das REAL-Token (das viel später abläuft) zu pushen, das wir als Ereignis-ID aus der Datenbank abgerufen haben. Es ist nicht wirklich notwendig, dass Javascript mit diesem Ereignis etwas anfängt - der Server beendet die Verbindung automatisch, aber Sie können das Ereignis abhören, wenn Sie mehr damit tun möchten.

Zu diesem Zeitpunkt ist die Verbindung SSE beendet, seit PHP das Skript beendet hat. Der Browser stellt die Verbindung jedoch automatisch wieder her (normalerweise 3 Sekunden). Diesmal wird die lastEventId ... gesendet, die wir vor dem Verbindungsabbruch auf den Tokenwert gesetzt haben. Bei der nächsten Verbindung wird dieser Wert als Token verwendet und die App wird wie erwartet ausgeführt. Es ist nicht wirklich notwendig, die Verbindung zu trennen, solange Sie das eigentliche Token als Ereignis-ID verwenden, wenn Sie Nachrichten/Ereignisse senden. Dieser Tokenwert wird sowohl beim Empfang des Browsers als auch bei jeder nachfolgenden Verbindung zum Server vollständig verschlüsselt über SSL übertragen. Der Wert, der "im Klartext" übermittelt wurde, ist innerhalb von Sekunden nach Erhalt und Verwendung verfallen und kann von niemandem mehr verwendet werden, der ihn entdeckt. Wenn jemand versucht, es zu benutzen, erhalten Sie eine 404 RESPONSE .

Wenn Sie die Ereignisstrom-ID bereits für einen anderen Zweck verwenden, funktioniert dies möglicherweise nicht sofort, wenn Sie das Auth-Token und den zuvor verwendeten Wert verketten und ihn in Variablen aufteilen, sodass er für den Rest des Elements transparent ist App. So etwas wie:

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

Diese Polyfill fügt Unterstützung für Authorization Header hinzu: https://github.com/Yaffle/EventSource/

So können Sie tun:

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

Wenn Sie diesen Zweig der Ereignisquellen-Polyfüllung verwenden, können Sie Autorisierungsheader hinzufügen, die der Beschreibung von Rafaelzlisboa ähneln: https://github.com/AlexGalays/EventSource#923b9a0998fcfd7753040e09aa83764b3cc0230d

Ï Ich weiß nicht, ob Sie den Authentifizierungsheader als zweites Argument wie im Beispiel von Rafaelzlisboa bereitstellen können. Ich habe es zum Laufen gebracht, indem ich ein Header-Objekt erstellt und meinen Berechtigungsheader wie folgt darin abgelegt habe:

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

2
Jeroen Dragt

die andere Möglichkeit, ein Auth-Token zu übergeben, ist über die URL als Abfrageparameter. Sie sollten jedoch die Sicherheit berücksichtigen. Fügen Sie auch Unterstützung für die Autorisierung durch Abfrageparameter auf der Serverseite hinzu.

2
Sergey Leyko

Der window.EventSource scheint noch keine zusätzlichen Header zu übergeben. Eine gute Nachricht ist, dass es einige andere Implementierungen von EventSource gibt, die zusätzliche Header unterstützen. Einige davon sind wie folgt:

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

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

es.onerror = err => {
    console.log('EventSource error: ', err);
};
0
Manoj Shrestha