【问题标题】:Angular 4 Implicit Flow Callback and Router GuardAngular 4 隐式流回调和路由器保护
【发布时间】:2017-10-14 16:17:33
【问题描述】:

在 Angular 4 中处理隐式流回调的最佳方法是什么?我希望 Guard 等到用户使用令牌重定向回并在 Guard 返回 true 或 false 之前存储它,在我被重定向回检查令牌之前,我得到了 Access Denied 路由几秒钟。有没有比我正在做的更好的方法来处理 AuthGuard,所以在身份验证完成之前我没有得到拒绝访问?

如何让路由器守卫等待重定向?

应用组件

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

  }    
}

身份验证服务

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

【问题讨论】:

    标签: angular angular4-router


    【解决方案1】:

    如果你想让你的守卫等待异步任务,你需要改变你的 AuthService 以返回一个你想要等待的异步任务中需要的可观察值和发射值,在你的例子中是 reduce()。之后在警卫中订阅。这样你就可以让你的守卫等待任何异步任务。

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

    身份验证服务的一部分

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

    【讨论】:

    • 这似乎工作得很好,但我还有另一个问题,我想可能与使用 Observables 有关。当我尝试在 Obserable 中使用注入服务(例如 isUserLoggedIn 方法中的 this.logger.log)时,它会在此方法的observer.js 中引发错误: Observable.prototype._trySubscribe = function (sink) { try { return this._subscribe (下沉); } catch (err) { sink.syncErrorThrown = true; sink.syncErrorValue = 错误; sink.error(错误); } };
    • 在构造函数中注册,在 observable 外部工作,但不在内部
    • 如果我尝试使用注入服务,我实际上只会在 .reduce 中遇到错误。
    • 它与 reduce 函数之外的逻辑一起工作得更好。我想我需要了解有关创建可观察对象的更多信息,但我正在关注observer.next 以返回一个值,并使用observer.complete 来完成我尚未使用但我想我现在明白了。
    【解决方案2】:

    CanActivate 方法可以返回 ObservablePromiseBoolean,Angular 会知道如何解包并异步处理所有内容。在将完成/失败的Observable 或已解决/拒绝的Promise 返回到 Angular 路由器并调用 this.naviationService.AccessDenied() 作为该异步函数的结果之前,您可以更改代码以检查必要的数据。

    【讨论】:

    • 如何与来自另一个 / 授权服务器的重定向一起工作?我通过数据 api 了解 Observables,但对重定向了解不多?你能提供一个代码示例吗?谢谢。
    • 基本上发生的事情是您的 AuthGuard 在 Angular 加载路由和组件之前被调用。在当前实现中,您的 CanActivate 立即 返回 false 并导航到 AccessDenied。它看起来像是使用该路由或外部重定向设置的东西,然后重新加载AuthGuard 再次触发并返回true 的页面。根据身份验证服务器的实现,我可以看到难以清除的地方。您可能想研究让窗口等待重定向,或检查resolve 路由器方法
    • ...以便在组件实际加载之前获取组件的数据。
    • 是的,在应用程序组件 ngOnInit() 中,要么重定向到身份验证服务器,要么调用 tryAuthorize,这将使令牌有效,然后重定向到我拥有的 Home 路由,所有 Guard 都会检查它们是否已登录,如果令牌存在于存储中,则返回 true 或 false。
    猜你喜欢
    • 1970-01-01
    • 2018-01-27
    • 2018-04-22
    • 1970-01-01
    • 2021-12-16
    • 2016-04-15
    • 2020-03-12
    • 2021-08-28
    • 2018-07-06
    相关资源
    最近更新 更多