Mit Hilfe von @ stely000, endete einige Kollegen und anderen Online-Communities ich auf den Punkt:
Da ich hier mein Code zum Abfangen des integrierten HTTP-Service von angular2/angular4 https://scotch.io/@kashyapmukkamala/using-http-interceptor-with-angular2 fand die Lösung bin mit unterscheidet sich ein wenig von anderen Lösungen, die ich gefunden habe. Die OAuthService
ich an verschiedenen Orten verweisen kann hier gefunden werden: https://github.com/manfredsteyer/angular-oauth2-oidc. Aufgrund von Abhängigkeiten in diesem Dienst zu Http
musste ich diesen Dienst nachher injizieren, um zirkuläre Abhängigkeiten zu vermeiden. Wenn Sie Fragen dazu haben, fragen Sie mich einfach und ich werde das auch beantworten. :)
Im Grunde ist dies meine Lösung für die refresh_token
Funktionalität einmal auszulösen, wenn der Back-End-Dienst mit 401 reagiert in drei Schritten erreicht:
eine beobachtbare erstellen, die this.postRequest zu vermeiden geteilt wird () um mehr als einmal zu triggern.
Erstellen Sie Anforderungsheader und fügen Sie einen Beitrag zum Endpunkt hinzu, wobei refresh_token behandelt wird.
Den freigegebenen Beobachter von Schritt 1 anhören. Wenn das Token aktualisiert wurde, extrahieren und aktualisieren Sie Daten im lokalen Speicher.
Jetzt, in meinem Konstruktor ich die beobachtbare shared:
constructor(
backend: ConnectionBackend,
defaultOptions: RequestOptions,
private injector: Injector
) {
super(backend, defaultOptions);
// Step 1: Create an observable that is shared to avoid this.postRequest() to trigger more than once at a time
this.refreshTokenObserver = Observable.defer(() => {
return this.postRequest();
}).share();
}
Dann erstelle ich eine Methode für die Anforderung der Buchung des access_token
(Hinweis zu aktualisieren: diese im Grunde eine Kopie des Codes ist, dass wird in der OAuthService
gehalten.Dies liegt daran, das Verfahren für den in diesem Dienst ist nicht öffentlich):
// This method will only be triggered once at a time thanks to she shared observer above (Step 1).
private postRequest(): Observable<any> {
// Step 2: Create request headers and add post to endpoint where refresh_token is handled.
let search = new URLSearchParams();
search.set('grant_type', 'refresh_token');
search.set('client_id', this.oauthService.clientId);
search.set('scope', '');
search.set('refresh_token', localStorage.getItem('refresh_token'));
let headers = new Headers();
headers.set('Content-Type', 'application/x-www-form-urlencoded');
let params = search.toString();
return super.post(this.oauthService.tokenEndpoint, params, { headers }).map(r => r.json());
}
Dann habe ich ein Verfahren zum Extrahieren von Daten und Aktualisierung des lokalen Speichers mit der Antwort dann Endpunkt:
// This method is triggered when the server responds with 401 due to expired access_token or other reasons
private refreshToken() {
// Step 3: Listen to the shared observer from step 1. When the token has been refreshed, extract and update data in localstorage
return this.refreshTokenObserver.do((tokenResponse) => {
localStorage.setItem("access_token", tokenResponse.access_token);
if (tokenResponse.expires_in) {
var expiresInMilliSeconds = tokenResponse.expires_in * 1000;
var now = new Date();
var expiresAt = now.getTime() + expiresInMilliSeconds;
localStorage.setItem("expires_at", "" + expiresAt);
}
if (tokenResponse.refresh_token) {
localStorage.setItem("refresh_token", tokenResponse.refresh_token);
}
},
(err) => {
console.error('Error performing password flow', err);
return Observable.throw(err);
});
}
In um die oben genannten Schritte, die anfängliche Anforderung zu initiieren muss ausgelöst werden und mit einem 401
reagieren:
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
return super.request(url, options).catch(error=> {
if (error.status === 401) {
// It the token has been expired trigger a refresh and after that continue the original request again with updated authorization headers.
return this.refreshToken().mergeMap(() => {
options = this.updateAuthHeader(options);
return super.request(url, options);
});
} else {
return Observable.throw(error);
}
});
}
Bonus: die Methode, die ich verwende für die A Aktualisierung uthorization Header verwendet grundsätzlich die Funktionalität in der OAuthService
oben erwähnt:
private updateAuthHeader(options: RequestOptionsArgs) {
options.headers.set('Authorization', this.oauthService.authorizationHeader());
return options;
}
Reflections/Gedanken: Die ursprüngliche Idee von meiner Seite die OAuthService
zu verwenden war Token zu aktualisieren. Dies war schwieriger als ich erwartet hatte aufgrund der Mischung aus Versprechungen und Observables. Ich kann wahrscheinlich die postRequest
Methode ändern, um die erwähnten Service-Methoden zu verwenden. Ich weiß nicht wirklich, was die bessere/sauberere Lösung sein könnte.
Auch denke ich, dass dies etwas für alle sein sollte, um eine einfache Lösung für zu finden. Dies war alleine schwer zu erreichen und ich danke allen, die mir geholfen haben (sowohl hier in SO, IRL als auch in anderen Gemeinden).
Nur sagen, wenn Ihre Anfrage tatsächlich erfolgreich ist (aka nicht 401), werfen Sie einen Fehler; was möglicherweise kein gewünschtes Verhalten ist. Schnelle Lösung: Entferne das 'else' – CozyAzure
Nun .. tue ich? Es ist innerhalb einer .catch() Hexe wirft den Fehler, nicht die erfolgreiche Anfrage –
Werfen Sie einen Blick auf meine Antwort hier, wo ich die gleiche Frage beantworten: https://stackoverflow.com/questions/42390773/angular2-get-and-cache -token-only-once/42394195 # 42394195 –