简体   繁体   中英

Why Angular Guards canActivate method is always returning false?

I'm doing a basic authentication for users after login, but weird behavior is happening, where canActivate is returning false, even though it is being called in the right time.

in Login Component

private login() {
  this.authService.login(this.loginForm.valid)
  .subscribe((data: any) => { // no need for pipe and map
    console.log("First");
    this.router.navigate(['/app']);
  })
}

in auth.service :

login({ username, password }: any): Observable<any>{
    this.clearData();

    const loginData = {
      'username': username,
      'password': password
    };

    return this.http.post(environment['baseURL'] + '/api/auth/login', loginData).pipe(
      map(
        (data: any) => {
          if (data['token'] && data['user']) {
            this.isLoggedIn.next(true); 
            this.token = data['token'];
            localStorage.setItem('token', this.token);
            localStorage.setItem('usermeta', JSON.stringify(this.userData));
            return true;
          } else {
            return false;
          }
        },
        (error: any)=>{
          return of(false)
        }
      )
      
    )
  }

  getToken(): string {
    return this.token;
  }

  hasToken() {
    return this.getToken() ? true : false;
  }

in auth.guard.service

constructor(
    private router: Router,
    private authService: AuthService,) {
    console.log("SECOND - canActivate") --> this is being printed on the success of the login call
  }

 canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    console.log(this.authService.hasToken(), ">> this.authService.hasToken() <<")
    if (this.authService.hasToken()) {
      return true;
    } else {

      // not logged in so redirect to login page with the return url
      this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
      return false;
    }

  }

In console tab, it shows "First" then "Second - canActivate" which tells that the token has already took its value on success of the call, However, for some reason the value returned by hasToken() method is false

在此处输入图像描述

To be honest, I can't quite spot what's wrong but there's a few things I'd write differently, so I'll suggest those and hopefully it refactors the issue away. Also console.log is async, so don't trust it to log in the correct order.

When using RxJS I recommend leaning towards functional and not using static member variables to pass values around. You should aim to chain everything so you don't have any unexpected async side effects.

This is not tested but should be correct. Check the inline comments. Hope it helps.

private login() {
  this.authService.login(this.loginForm.valid)
  .subscribe((data: any) => { // no need for pipe and map
    console.log("First");
    this.router.navigate(['/app']);
  })
}
export class AuthService {

  public isLoggingIn = new BehaviorSubject<boolean>(false);
  public isLoggedIn = new BehaviorSubject<boolean>(false);

  constructor() { }

  // You were returning Observables, not booleans. Help TypeScript help you - don't use 'any'.
  public login(loginData: { username: string, password: string }): Observable<boolean> { 
    this.clearData();

    this.isLoggingIn.next(true);

    return this.http.post(environment['baseURL'] + '/api/auth/login', loginData)
      .pipe(
        map(
          (data: any) => {
            if (data['token'] && data['user']) {
              this.isLoggedIn.next(true);
              this.isLoggingIn.next(false);
              this.token = data['token'];
              localStorage.setItem('token', this.token);
              localStorage.setItem('usermeta', JSON.stringify(this.userData));
              return true // no 'of'
            } else {
              return false // no 'of'
            }
          }
        ),
        catchError((error: any) => {
          this.isLoggingIn.next(false);
          return of(false) // catchError expects a new observable so we do use 'of' here.
        })
      )
  }

}
export class AuthGuard implements CanActivate {

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

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
    return this.authService.isLoggedIn.pipe(
      // always important to handle the transition state
      // skip if currently logging in (assumes a second emission once logged in)
      skipUntil(this.authService.isLoggingIn.pipe(filter(isLoggingIn => !isLoggingIn))) 
    ).pipe(
      map(isLoggedIn => {
        if (isLoggedIn) {
          return true;
        } else {
          return this.router.createUrlTree(['/login'], { queryParams: { returnUrl: state.url } })
        }
      })
    )
  }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM