首页 文章

在auth-guard的canActivate中使用BehaviorSubject

提问于
浏览
2

What I want to achieve: 我想使用BehaviorSubject在我的应用程序中共享身份验证状态 . 我使用身份验证状态,例如在auth-guard内部,以防止用户在用户已经过身份验证时访问登录/注册页面 .

Problem: 因为BehaviorSubject有一个初始值,它是假的(未登录),似乎auth-guard取第一个值,而不是等待uid-sync .

AuthInfo(Auth状态存储):

export class AuthInfo {

  constructor(public uid: string) {}

  isLoggedIn() {
    return !!this.uid;
  }
}

AuthService:

@Injectable()
export class AuthService {

  static UNKNOWN_USER = new AuthInfo(null);
  authInfo$: BehaviorSubject<AuthInfo> = new BehaviorSubject<AuthInfo>(AuthService.UNKNOWN_USER);

  constructor(private af: AngularFire) {
    this.af.auth.subscribe(auth => {
      if (auth) {
        console.log('got the uid');
        this.authInfo$.next(new AuthInfo(auth.uid));
      } else {
        this.authInfo$.next(AuthService.UNKNOWN_USER);
      }
    });
  }

  logIn(email: string, password: string): Promise<FirebaseAuthState> {
    return this.af.auth.login({email: email, password: password});
  }
}

AuthGuard:

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService,
              private router: Router) {
  }

  canActivate(): Observable<boolean> {
    return this.authService.authInfo$.map(authInfo => {
      if (authInfo.isLoggedIn()) {
        this.router.navigate(['/user'])
      }
      return !authInfo.isLoggedIn();
    });
  }
}

所以canActivate用 authInfo.isLoggedIn() 处理 false 并且在一小段时间之后我在控制台中看到 Got the uid . 任何想法如何防止第一个 false ?我认为这里正确使用BehaviorSubject,因为它允许我们设置初始状态 . 但是,auth-guard将始终收到false(初始值) . 就在那之后

this.authInfo$.next(new AuthInfo(auth.uid));

当canActivate方法已经完成时将触发 .

1 回答

  • 3

    正如其名称所示,Guard的canActivate方法可以解决激活特定路线的问题 .

    据我所知,从提供的代码中,您尝试从服务器检索auth uid 时将用户重定向到 /user 路由 . 为了实现这一点,一旦检索到auth uid ,你需要启动重定向到所需的路由 - 例如登录后,让您的警卫完成其工作,启用或拒绝访问该路线 .

    在对事情进行排序之后,这里是改变的代码和结构的演练:

    AuthInfo类:

    // No changes.
    

    AuthService:

    @Injectable()
    export class AuthService {
    
      static UNKNOWN_USER = new AuthInfo(null);
      authInfo$: BehaviorSubject<AuthInfo> = new BehaviorSubject<AuthInfo>(AuthService.UNKNOWN_USER);
    
      constructor(private af: AngularFire) { }
    
      logIn(email: string, password: string): Promise<FirebaseAuthState> {
        return this.af.auth.login({email: email, password: password});
      }
    
      getAuthInfo(): Observable<AuthInfo> {
        return this.af.auth.map(auth => {
          if(auth) {
            console.log('got the uid');
            let authInfo = new AuthInfo(auth.uid);
            this.authInfo$.next(authInfo);
            return authInfo;
          } 
          else {
            this.authInfo$.next(AuthService.UNKNOWN_USER);
            return AuthService.UNKNOWN_USER;
          }
        });
      }
    }
    

    AuthGuard:

    @Injectable()
    export class AuthGuard implements CanActivate {
    
      constructor(private authService: AuthService,
                  private router: Router) {
      }
    
      canActivate(): Observable<boolean> | boolean {
    
        // get the most recent value BehaviorSubject holds
        if (this.authService.authInfo$.getValue().isLoggedIn()) {
          // can access targeted route
          return true;
        }
    
        /*
        User is not logged in as stored authInfo indicates, 
        but in case the page has been reloaded, the stored value is lost, 
        and in order to get real auth status we will perform the server call,
        (authService.getAuthInfo method will automatically update the BehaviorSubject value, 
        and next time the protected route is accessed, no additional call will be made - until 
        the next reloading).
        */
    
        return this.authService.getAuthInfo()
            .map((authInfo: AuthInfo) => {
              if(authInfo.isLoggedIn()) {
                // can access targeted route
                return true;
              }
    
              this.router.navigate(['login']); // redirect to login screen
              return false;
            });
      }
    }
    

相关问题