2017-05-15 4 views
12

Was ist der beste Weg, um den impliziten Fluss Callback in Angular 4 zu behandeln? Ich möchte, dass der Wächter wartet, bis der Benutzer mit dem Token zurückgeleitet wird, und er wird gespeichert, bevor der Wächter "Wahr" oder "Falsch" zurückgibt. Ich bekomme die Route "Zugriff verweigert" für ein paar Sekunden, bevor ich zurückgeleitet werde, um das Token zu überprüfen. Gibt es eine bessere Möglichkeit, mit dem AuthGuard umzugehen als mit dem, was ich gerade mache? Ich bekomme den Zugriff nicht verweigert, bevor die Authentifizierung abgeschlossen ist?Angular 4 Impliziter Fluss Callback und Router Guard

Wie kann ich den Router Guard warten auf die Umleitung?

AppComponent

ngOnInit() {  

     //if there is a hash then the user is being redirected from the AuthServer with url params 
     if (window.location.hash && !this.authService.isUserLoggedIn()) {  

      //check the url hash 
      this.authService.authorizeCallback(); 

     } 
     else if (!this.authService.isUserLoggedIn()) {   

      //try to authorize user if they aren't login 
      this.authService.tryAuthorize();  

    }  
} 

AuthSerivce

tryAuthorize() { 
     //redirect to open id connect /authorize endpoint 
     window.location.href = this.authConfigService.getSignInEndpoint(); 
    } 

    authorizeCallback() {  

     let hash = window.location.hash.substr(1); 

     let result: any = hash.split('&').reduce(function (result: any, item: string) { 
      let parts = item.split('='); 
      result[parts[0]] = parts[1]; 
      return result; 
     }, {}); 


     if (result.error && result.error == 'access_denied') { 
      this.navigationService.AccessDenied(); 
     } 
     else { 

      this.validateToken(result); 
     } 
    } 


    isUserLoggedIn(): boolean {  
     let token = this.getAccessToken(); 

     //check if there is a token  
     if(token === undefined || token === null || token.trim() === '') 
     { 
      //no token or token is expired; 
      return false; 
     } 

     return true; 
    } 


    getAccessToken(): string {    

     let token = <string>this.storageService.get(this.accessTokenKey); 


     if(token === undefined || token === null || token.trim() === '') 
     { 
      return ''; 
     } 

     return token; 
    } 

    resetAuthToken() { 
     this.storageService.store(this.accessTokenKey, ''); 
    } 

    validateToken(tokenResults: any) {   

     //TODO: add other validations   

     //reset the token 
     this.resetAuthToken(); 

     if (tokenResults && tokenResults.access_token) { 

      //store the token 
      this.storageService.store(this.accessTokenKey, tokenResults.access_token); 

      //navigate to clear the query string parameters 
      this.navigationService.Home(); 

     } 
     else { 
      //navigate to Access Denied 
      this.navigationService.AccessDenied(); 
     } 

    } 
} 

AuthGuard

canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot){ 

    var hasAccess = this.authService.isUserLoggedIn();   

    if(!hasAccess) 
    { 
     this.naviationService.AccessDenied(); 
     return false; 
    } 
    return true;  
    } 

Antwort

5

Wenn Sie möchten, dass Ihr Wächter auf eine asynchrone Aufgabe wartet, müssen Sie Ihren AuthService ändern, um einen beobachtbaren und emittierten Wert zurückzugeben, den Sie innerhalb der asynchronen Aufgabe benötigen, die Sie warten möchten, in Ihrem Fall reduce(). Danach machen Sie ein Abonnement in der Wache. Auf diese Weise können Sie Ihren Wächter auf jede asynchrone Aufgabe warten lassen.

AuthGuard

canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot){ 

    this.authService.isUserLoggedIn().map(logged => { 
     if (logged) { 
      return true; 
     } else { 
      this.naviationService.AccessDenied(); 
      return false; 
     } 
    }).catch(() => { 
     this.naviationService.AccessDenied(); 
     return Observable.of(false); 
    }); 
    } 
} 

Teil AuthService

isUserLoggedIn(): Observable<boolean> { 
    return new Observable((observer) => { 
     let hash = window.location.hash.substr(1); 

     let result: any = hash.split('&').reduce(function (result: any, item: string) { 
     let parts = item.split('='); 
     result[parts[0]] = parts[1]; 

     if (result.error && result.error == 'access_denied') { 
      observer.next(false); 
      observer.complete(); 
     } 
     else { 
      this.validateToken(result); 
     } 
     let token = this.getAccessToken(); 

     //check if there is a token  
     if(token === undefined || token === null || token.trim() === '') 
     { 
      //no token or token is expired; 
      observer.next(false); 
      observer.complete(); 
     } 

     observer.next(true); 
     observer.complete(); 
     }, {}); 
    }); 
} 
+0

Das scheint gut zu funktionieren, aber ich habe ein anderes Problem, das mit der Verwendung von Observables zusammenhängt. Wenn ich versuche, einen injizierten Dienst innerhalb der Obserable wie this.logger.log innerhalb der isUserLoggedIn-Methode zu verwenden, wird ein Fehler in observer.js für diese Methode ausgegeben: Observable.prototype._trySubscribe = Funktion (Senke) { try { Rückgabe this._subscribe (sink); } catch (err) { sink.syncErrorThrown = true; sink.syncErrorValue = err; sink.fehler (err); } }; – Fab

+0

Es ist im Konstruktor registriert und funktioniert außerhalb der beobachtbaren aber nicht innerhalb – Fab

+0

Ich bekomme eigentlich nur Fehler innerhalb der .reduce, wenn ich versuche, einen injizierten Dienst zu verwenden. – Fab

1

Die CanActivate Methode kann eine Observable, Promise oder 10 zurückgeben, und Angular wird wissen, um es auszupacken und alles async zu handhaben. Sie können Ihren Code ändern, um nach den erforderlichen Daten zu suchen, bevor Sie entweder einen abgeschlossenen/fehlgeschlagenen Observable oder einen aufgelösten/zurückgewiesenen Promise an den Angular-Router senden und this.naviationService.AccessDenied() als Ergebnis dieser asynchronen Funktion aufrufen.

+1

Wie funktioniert das mit einer Umleitung von einem anderen/Autorisierungs-Server? Ich verstehe die Observables mit einer Daten-API, aber nicht so sehr mit einer Weiterleitung? Können Sie vielleicht ein Codebeispiel angeben? Vielen Dank. – Fab

+0

Im Grunde wird Ihr 'AuthGuard' aufgerufen, bevor Angular die Route und die Komponente lädt. In der aktuellen Implementierung gibt Ihr 'CanActivate' * sofort * false zurück und navigiert zu' AccessDenied'. Es sieht aus wie etwas, das mit dieser Route oder einer externen Weiterleitung eingerichtet wurde, lädt dann die Seite neu, auf der der 'AuthGuard' erneut ausgelöst wird, und gibt true zurück. Abhängig von der Implementierung des Authentifizierungsservers kann ich sehen, wo dies schwierig zu eliminieren ist. Vielleicht möchten Sie überprüfen, ob das Fenster auf eine Weiterleitung wartet, oder überprüfen Sie die Router-Methode "resolve" – joh04667

+0

..., um Daten für eine Komponente zu erhalten, bevor sie tatsächlich geladen wird. – joh04667

Verwandte Themen