Angular 5:身份验证守卫自动导航到指定的组件

Gee*_*san 1 authentication angular-routing angular-services angular

我已经设置了一个带有 firebase 电子邮件和密码登录的身份验证防护,问题是它会自动触发到指定组件的路由。

我已经实现了身份验证保护并将其设置在正确的模块提供程序中(因为我的应用程序中有很多提供程序)。这是我的身份验证服务:

import { Injectable } from '@angular/core';
import { Router, Route ,CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild, CanLoad } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { SimpleAuthService } from './simple-auth.service';

@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild, CanLoad {
  constructor(private authService: SimpleAuthService) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    const url: string = state.url;
    return this.checkLogin(url);
  }

  canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    return this.canActivate(route, state);
  }

  canLoad(route: Route): Observable<boolean> {
    const url: string = route.path;
    return this.checkLogin(url);
  }

  checkLogin(url: string): Observable<boolean> {
    return this.authService.isLoggedIn(url);
  }
}
Run Code Online (Sandbox Code Playgroud)

这是我注入它的组件类(登录组件):

import { Component, OnInit, ElementRef } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { SimpleAuthService } from '../../core/simple-auth.service';

declare var $: any;

@Component({
    selector: 'app-login-cmp',
    templateUrl: './login.component.html'
})

export class LoginComponent implements OnInit {
    private toggleButton: any;
    private sidebarVisible: boolean;
    private nativeElement: Node;
    private email  ='user@dev.org'
    private password ='useruser';


    constructor(private element: ElementRef, public authService: SimpleAuthService,
        private router: Router, private route: ActivatedRoute) {
            if (this.authService.login(this.email,this.password)) {
                this.router.navigate(['dashboard']);
            } else {
                this.nativeElement = element.nativeElement;
                this.sidebarVisible = false;
            }
        }

    ngOnInit() {
        this.login(this.email, this.password);
        var navbar : HTMLElement = this.element.nativeElement;
        this.toggleButton = navbar.getElementsByClassName('navbar-toggle')[0];

        setTimeout(function() {
            // after 1000 ms we add the class animated to the login/register card
            $('.card').removeClass('card-hidden');
        }, 700);
    }
    sidebarToggle() {
        var toggleButton = this.toggleButton;
        var body = document.getElementsByTagName('body')[0];
        var sidebar = document.getElementsByClassName('navbar-collapse')[0];
        if (this.sidebarVisible == false) {
            setTimeout(function() {
                toggleButton.classList.add('toggled');
            }, 500);
            body.classList.add('nav-open');
            this.sidebarVisible = true;
        } else {
            this.toggleButton.classList.remove('toggled');
            this.sidebarVisible = false;
            body.classList.remove('nav-open');
        }
    }

    login(username: string, password: string): void {
        this.authService.login(username, password).then(_ => {
          const redirectUrl: string = this.authService.redirectUrl || 'dashboard';
          this.router.navigate([redirectUrl]);
        });
      }
}
Run Code Online (Sandbox Code Playgroud)

这是我使用 firebase 制作的身份验证服务:

import { Injectable } from '@angular/core';
import { Router, Route ,CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild, CanLoad } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AngularFireAuth } from 'angularfire2/auth';
import { User, UserCredential } from '@firebase/auth-types';
import { take, map, tap } from 'rxjs/operators';

@Injectable()
export class SimpleAuthService {
  user: Observable<User>;
  redirectUrl: string;

  constructor(private afAuth: AngularFireAuth, private router: Router) {
    this.user = this.afAuth.authState;
  }

  getUser(): Observable<User> {
    return this.user.pipe(take(1));
  }

  isLoggedIn(redirectUrl: string): Observable<boolean> {
    return this.user.pipe(
      take(1),
      map(authState => !!authState),
      tap(authenticated => {
        if (!authenticated) {
          this.redirectUrl = redirectUrl;
          this.router.navigate(['/']);
        }
      })
    );
  }

  login(username: string, password: string): Promise<UserCredential> {
    return this.afAuth.auth.signInWithEmailAndPassword(username, password);
  }

  logout(): Promise<boolean> {
    return this.afAuth.auth.signOut().then(() => this.router.navigate(['/login']));
  }
}
Run Code Online (Sandbox Code Playgroud)

你知道我在哪里犯了错误吗?

Ale*_*sky 5

基本问题是您的login()方法是异步的,但您正在尝试检查结果,就好像它是同步方法一样。此外,login()当前甚至没有返回任何内容void,因此不会有任何结果需要检查。无论哪种方式,即使结果作为 返回Promise<T>,您也需要使用then()来访问结果/成功,并catch()在组件中相应地处理错误。您根本无法在这样的if()语句中检查结果。在基本层面上,如果你试图检查一个Promise<T>在 if 语句中返回 a 的函数,它总是真实的,无论是否catch()在几毫秒后被触发。

function login(): Promise<boolean> {
    return Promise.resolve(true);
}

if(login()) {} // this is always true regardless of `catch()` is triggered sometime in the future
Run Code Online (Sandbox Code Playgroud)

使用 angularfire2,您可以实时跟踪用户的身份验证状态Observable<User>,可用于诸如canActivate(). 这是您可以采用的一种方法,它Observable<User>在 auth 服务中公开,可用于检查登录状态,获取当前用户,甚至在模板中使用async管道显示诸如用户头像图像之类的内容。有RxJS运营商,如taptake以及map帮助避免不必要的订阅保持活跃。使用这些方法主要是返回Observable<T>,您可以另外在其他服务或组件中管道附加操作符/操作以充分利用 RxJS。

认证服务:

import { AngularFireAuth } from 'angularfire2/auth';
import { User, UserCredential } from '@firebase/auth-types';

@Injectable()
export class AuthService {
  user: Observable<User>;
  redirectUrl: string;

  constructor(private afAuth: AngularFireAuth, private router: Router) {
    this.user = this.afAuth.authState;
  }

  getUser(): Observable<User> {
    return this.user.pipe(take(1));
  }

  isLoggedIn(redirectUrl: string): Observable<boolean> {
    return this.user.pipe(
      take(1),
      map(authState => !!authState),
      tap(authenticated => {
        if (!authenticated) {
          this.redirectUrl = redirectUrl;
          this.router.navigate(['/login']);
        }
      })
    );
  }

  login(username: string, password: string): Promise<UserCredential> {
    return this.afAuth.auth.signInWithEmailAndPassword(email, password);
  }

  logout(): Promise<boolean> {
    return this.afAuth.auth.signOut().then(() => this.router.navigate(['/login']));
  }
}
Run Code Online (Sandbox Code Playgroud)

授权守卫:

@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild, CanLoad {
  constructor(private authService: AuthService) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    const url: string = state.url;
    return this.checkLogin(url);
  }

  canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    return this.canActivate(route, state);
  }

  canLoad(route: Route): Observable<boolean> {
    const url: string = route.path;
    return this.checkLogin(url);
  }

  checkLogin(url: string): Observable<boolean> {
    return this.authService.isLoggedIn(url);
  }
}
Run Code Online (Sandbox Code Playgroud)

成分:

export class LoginComponent implements OnInit {
  constructor(private router: Router, public authService: AuthService) { }

  ngOnInit() {
    this.login(this.email, this.password);
  }

  login(username: string, password: string): void {
    this.authService.login(username, password).then(_ => {
      const redirectUrl: string = this.authService.redirectUrl || '/some-default-route';
      this.router.navigate([redirectUrl]);
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

总体建议,您将希望更好地利用服务进行组件之间的通信。您在 JavaScript 和 jQuery 中大量使用 DOM 操作,这违背了 Angular 的目的,并且可能会导致问题,因为由于组件生命周期和渲染,元素在您预期时不可用。使用服务和 RxJS,您可以通过指令ngClass设置 CSS 类。查看关于Parent 和 children 通过服务进行通信的文档,了解组件如何在基本级别进行通信的示例。如果它是您需要的 Bootstrap,您应该考虑基于 Angular 的 Bootstrap 组件/库,这些组件/库与 Angular 组件生命周期挂钩,并且不依赖于 jQuery。

更新:

根据您提供的更新代码,您仍在尝试login()使用if构造函数中的语句检查异步 Promise 是否成功。这将始终导致重定向,因为login()它会返回一个真实值的承诺。而是尝试在您返回的承诺上使用then()/来响应 Firebase 的成功/错误:catch()LoginComponentsignInWithEmailAndPassword()

this.authService.login(this.email, this.password)
  .then(result => this.router.navigate(['dashboard']))
  .catch(err => {
    this.nativeElement = element.nativeElement;
    this.sidebarVisible = false;
  });
Run Code Online (Sandbox Code Playgroud)

希望这有帮助!