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

查看:24
本文介绍了在 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 有一个初始值,它是假的(未登录),似乎 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(授权状态存储):

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

So 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.

身份验证服务:

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

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

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