在auth-guard的canActivate中使用BehaviorSubject [英] Using BehaviorSubject in auth-guard's canActivate

查看:508
本文介绍了在auth-guard的canActivate中使用BehaviorSubject的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要实现的目标:我想使用BehaviorSubject在我的应用程序中共享身份验证状态.我使用身份验证状态内置在auth-guard中,以防止用户在已经通过身份验证时访问登录/注册页面.

What I want to achieve: I want to share authentication state across my application using BehaviorSubject. I use the authentication state e.g. inside an auth-guard to prevent the user from visiting login/register pages when the user already is authenticated.

问题::因为BehaviorSubject具有一个初始值,该初始值为false(未登录),所以auth-guard似乎采用了该第一个值,而不是等待uid-sync.

Problem: because the BehaviorSubject has a initial value, which is false (not logged in), it seems that the auth-guard takes this first value, instead of waiting for the uid-sync.

AuthInfo (身份验证状态存储区):

AuthInfo (Auth state store):

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(初始值).之后

So canActivate is processed with authInfo.isLoggedIn() being false and after a fraction of a second I see Got the uid in the console. Any ideas how to prevent the first false? I think that BehaviorSubject is correctly used here, because it allows us to set an initial state. However the auth-guard will always receive false (the initial value). Right after that the

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

当canActivate方法已经完成时,

将触发.

will trigger, when the canActivate method was already finished.

推荐答案

Guard的 canActivate 方法可解决激活特定路由的尝试.

Guard's canActivate method, as its name suggests, resolves upon the attemption to activate specific route.

据提供的代码了解,您正在尝试从服务器上获取auth uid时将用户重定向到/user路由.为此,一旦检索到auth uid,您需要启动重定向到所需的路由-例如登录后,让您的警卫工作,启用或拒绝对路由的访问.

As I understand from the code provided, you're trying to redirect user to the /user route upon retrieving auth uid from the server. To achieve that, you need to initiate redirecting to the desired route once the auth uid is retrieved - e.g. after logging in, and let your guard do its job, enable or deny the access to the route.

整理完所有内容后,以下是更改后的代码和结构的演练:

After sorting things out, here is the walk-through the changed code and structure:

AuthInfo类:

AuthInfo class:

// No changes.

AuthService:

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:

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

这篇关于在auth-guard的canActivate中使用BehaviorSubject的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆