Угловой 5: Аутентификация охранника автоматически перейти к указанному компоненту - PullRequest
0 голосов
/ 12 сентября 2018

Я установил защиту аутентификации с помощью электронной почты и пароля для входа в 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  ='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]);
        });
      }
}

это мойслужба аутентификации сделана с использованием 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']));
  }
}

У вас есть идеи, где я допустил ошибку?

1 Ответ

0 голосов
/ 12 сентября 2018

Основная проблема в том, что ваш метод login() является асинхронным, но вы пытаетесь проверить результат, как если бы он был синхронным методом.Кроме того, login() в настоящее время даже не возвращает ничего void, поэтому не будет никакого результата для проверки.В любом случае, даже если результат был возвращен как Promise<T>, вам нужно будет использовать then() для доступа к результатам / успеху и catch() для соответствующей обработки ошибок в компоненте.Вы просто не сможете проверить результаты в таком выражении if().На базовом уровне, если вы пытаетесь проверить функцию, возвращающую Promise<T> в операторе if, это всегда будет правдой, независимо от того, будет ли 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().Вот подход, который вы могли бы использовать, который выставляет Observable<User> в сервисе аутентификации, который можно использовать для проверки статуса входа в систему, получения текущего пользователя или даже в шаблоне для отображения чего-то вроде изображения аватара пользователя с конвейером async,Есть операторы RxJS, такие как tap, take и 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']));
  }
}

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

Общая рекомендация, вы захотите воспользоваться лучшими услугами для связи между компонентами.Вы интенсивно используете как DOM-манипуляции с JavaScript, так и jQuery, что противоречит цели Angular и, вероятно, вызовет проблемы, так как элементы будут недоступны, когда вы ожидаете из-за жизненного цикла компонента и рендеринга.Используя сервисы и RxJS, вы можете устанавливать классы CSS с помощью директив ngClass .Ознакомьтесь с документацией по . Родители и дети общаются через службу , чтобы узнать, как компоненты могут взаимодействовать на базовом уровне.Если вам нужен именно Bootstrap, вы должны рассмотреть компоненты / библиотеки Bootstrap на основе Angular, которые подключаются к жизненному циклу компонента Angular и не зависят от jQuery.

Обновление:

На основе предоставленного вами обновленного кода вы все еще пытаетесь проверить успешность асинхронного Promise login() с помощью оператора if в своем конструкторе,Это всегда будет вызывать перенаправление, так как login() возвращает обещание, которое будет иметь истинное значение.Вместо этого попробуйте использовать then() / catch() в возвращенном обещании в вашем LoginComponent, чтобы ответить на успех / ошибку signInWithEmailAndPassword() в Firebase:

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

Надеюсь, это поможет!

...