Angular 5:身份验证防护自动导航到特定组件

问题描述 投票:0回答:1

我已经设置了一个带有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);
  }
}

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

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  ='[email protected]'
    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]);
        });
      }
}

这是我使用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']));
  }
}

你知道我弄错了吗?

angular authentication angular-routing angular-services
1个回答
3
投票

基本问题是你的login()方法是异步的,但是你试图检查结果,好像它是一个同步方法。此外,login()甚至没有返回任何目前void,所以没有任何结果可以检查。无论哪种方式,即使结果作为Promise<T>返回,您也需要使用then()来访问结果/成功,并使用catch()来相应地处理组件中的错误。您根本无法在if()语句中检查结果。在基本级别,如果您尝试检查在if语句中返回Promise<T>的函数,则无论是否在几毫秒之后触发catch(),它都将始终是真实的。

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

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

使用angularfire2,您可以实时跟踪用户的身份验证状态,作为Observable<User>,可以在canActivate()等方法中使用。以下是一种方法,可以在auth服务中公开Observable<User>,可用于检查登录状态,获取当前用户,甚至可以在模板中使用async管道显示类似用户头像的内容。有一些RxJS运算符,如taptakemap,可以帮助避免不必要地保持订阅活跃。使用这些方法主要返回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']));
  }
}

Auth Guard:

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

零件:

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

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

更新:

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

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

希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.