Ich verwende WSO2 als meinen Identity Provider (IDP). Es setzt das JWT in einen Header namens "X-JWT-Assertion".
Um dies in das ASP.NET Core-System einzuspeisen, habe ich ein OnMessageReceived
Ereignis hinzugefügt . Dadurch kann ich token
den Wert auf den im Header angegebenen Wert einstellen .
Hier ist der Code, den ich dazu machen muss (der Schlüsselteil sind die letzten 3 Zeilen des Codes ohne Klammern):
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddJwtBearer(async options =>
{
options.TokenValidationParameters =
await wso2Actions.JwtOperations.GetTokenValidationParameters();
options.Events = new JwtBearerEvents()
{
// WSO2 sends the JWT in a different field than what is expected.
// This allows us to feed it in.
OnMessageReceived = context =>
{
context.Token = context.HttpContext.Request.Headers["X-JWT-Assertion"];
return Task.CompletedTask;
}
}
};
Dies alles funktioniert perfekt, bis auf den ersten Anruf nach dem Start des Dienstes. Um klar zu sein, jeder Anruf, bis auf den ersten, funktioniert genau so, wie ich es möchte. (Es legt den Token ein und aktualisiert das User
Objekt wie benötigt.)
Aber beim ersten Anruf wird der OnMessageReceived
nicht getroffen. Und das User
Objekt in meinem Controller ist nicht eingerichtet.
Ich habe HttpContext
nach diesem ersten Aufruf gesucht und der Header "X-JWT-Assertion" befindet sich in der Request.Headers
Liste (mit dem JWT darin). Aber aus irgendeinem Grund ist die OnMessageReceived
Veranstaltung nicht dafür vorgesehen.
Wie kann ich OnMessageReceived
zum ersten Aufruf eines Servicevorgangs für meinen Service aufgerufen werden?
WICHTIGER HINWEIS: Ich finde heraus , dass das Problem war async
await
in AddJwtBearer
. (Siehe meine Antwort unten.) Das wollte ich wirklich von dieser Frage.
Da jedoch eine Prämie nicht cancled werden kann, werde ich vergeben noch die Prämie für jeden, der einen Weg zu verwenden , zeigen kann , AddJwtBearer
mit async
await
denen es einen tatsächlichen wartet HttpClient
Anruf. Oder zeigen Sie die Dokumentation, warum async
await
nicht mit verwendet werden soll AddJwtBearer
.
quelle
async
await
daran, dass dieAddJwtBearer
(und zugrunde liegendeAuthenticationBuilder.AddSchemeHelper
) keine asynchronen Aufrufe erwarten - es werden lediglich IConfigureOptions zu Serices hinzugefügt.OnMessageReceived
, auf der anderen Seite - wird am erwartet. Ich frage mich also, ob Sie diesesOnMessageReceived
Lambda möglicherweise asynchron machen, Ihren http-Aufruf in denOnMessageReceived
Body verschieben und dort irgendwie Cache-Ergebnisse erzielen könnten .Antworten:
UPDATE:
Das Lambda ist eine
Action
Methode. Es gibt nichts zurück. Der Versuch, Asynchronität darin zu machen, ist also nicht möglich, ohne dass es Feuer und Vergessen ist.Diese Methode wird auch beim ersten Aufruf aufgerufen. Die Antwort ist also, alles, was Sie für diese Methode benötigen, im Voraus aufzurufen und zwischenzuspeichern. (Ich habe jedoch keine Nicht-Hack-Methode gefunden, um mit Abhängigkeiten injizierte Elemente für diesen Aufruf zu verwenden.) Während des ersten Aufrufs wird dieses Lambda aufgerufen. Zu diesem Zeitpunkt sollten Sie die benötigten Werte aus dem Cache ziehen (wodurch der erste Aufruf nicht wesentlich verlangsamt wird).
Das habe ich endlich herausgefunden.
Das Lambda für
AddJwtBearer
funktioniert nicht mitasync
await
. Mein Anrufawait wso2Actions.JwtOperations.GetTokenValidationParameters();
wartet in Ordnung, aber die Anrufpipeline wird fortgesetzt, ohne aufAddJwtBearer
den Abschluss zu warten .Mit
async
await
der Anrufreihenfolge geht das so:AddJwtBearer
wird genannt.await wso2Actions.JwtOperations.GetTokenValidationParameters();
wird genannt.GetTokenValidationParameters()
ruft einHttpClient
mit aufawait
.HttpClient
führt einen erwarteten Anruf durch, um den öffentlichen Signaturschlüssel des Emittenten zu erhalten.HttpClient
wartet, wird der Rest des ursprünglichen Anrufs durchlaufen. Es wurden noch keine Ereignisse eingerichtet, daher wird die Anrufpipeline wie gewohnt fortgesetzt.OnMessageReceived
Ereignis zu "überspringen" .HttpClient
erhält die Antwort mit dem öffentlichen Schlüssel.AddJwtBearer
weiter.OnMessageReceived
Ereignis ist eingerichtet.AddJwtBearer
wird nur beim ersten Anruf aufgerufen.)Wenn also das Warten stattfindet (in diesem Fall trifft es schließlich einen HttpClient-Aufruf, um den Issuer Signing Key zu erhalten), wird der Rest des ersten Aufrufs durchlaufen. Da noch kein Ereignis eingerichtet wurde, kann der Handler nicht aufgerufen werden.
Ich habe das Lambda so geändert
AddJwtBearer
, dass es nicht asynchron ist, und es hat gut funktioniert.Anmerkungen:
Zwei Dinge scheinen hier seltsam:
AddJwtBearer
dies beim Start aufgerufen wird, nicht beim ersten Aufruf des Dienstes.AddJwtBearer
dies eineasync
Lambda-Signatur nicht unterstützen würde, wenn sie das Warten nicht korrekt anwenden könnte.Ich bin nicht sicher, ob dies ein Fehler ist oder nicht, aber ich habe ihn nur für den Fall veröffentlicht: https://github.com/dotnet/aspnetcore/issues/20799
quelle
OnMessageReceived
.Sie können verwenden
GetAwaiter().GetResult()
, um beim Start asynchronen Code auszuführen. Es wird den Thread blockieren, aber es ist in Ordnung, da es nur einmal ausgeführt wird und sich beim Start der Anwendung befindet.Wenn Sie den Thread jedoch nicht blockieren und darauf bestehen möchten
await
, die Optionen abzurufen, können Sieasync
await
in verwendenProgram.cs
, um Ihre Optionen abzurufen, sie in einer statischen Klasse zu speichern und beim Start zu verwenden.quelle
Der Grund, warum Ihre ersten paar Anfragen nicht ausgelöst werden können,
OnMessageReceived
liegt nicht an dem vonasync void
Ihnen verwendeten Delegaten, sondern an der Reihenfolge, in der die Parameter geladen und die Ereignisse angehängt werden.Sie hängen Handler an Ereignisse nach an
await
, was bedeutet, dass Sie hier eine Race-Bedingung erstellt haben, dass, wenn beispielsweise eine Anforderung eintrifft, bevor derawait
abgeschlossen ist, überhaupt kein Event-Handler angehängt istOnMessageReceived
.Um dies zu beheben, sollten Sie Ereignishandler vor dem ersten anhängen
await
. Dies garantiert, dass Sie immer Event-Handler habenOnMessageReceived
.Versuchen Sie diesen Code:
quelle