【问题标题】:Route guard with auth.service使用 auth.service 进行路由保护
【发布时间】:2018-06-09 22:22:12
【问题描述】:

我正在尝试创建一个路由保护,以确保某些延迟加载模块的安全,我有一个带有 BehaviorSubject 的身份验证服务,它包含当前用户和 JWT 令牌

当我调用警卫时,它首先获取当前用户的默认值,然后仅在第二次尝试它允许用户到达路线。

auth.service.ts:

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpResponse, HttpHeaders } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { map, skip, takeLast, last } from 'rxjs/operators';

import { User } from '../shared/user';
import { environment } from '../../environments/environment';


@Injectable()
export class AuthService {
  private loginUserToken: BehaviorSubject<string>;
  private currentUser: BehaviorSubject<User>;
  constructor(private http: HttpClient) {
    // service init
    this.loginUserToken = new BehaviorSubject(undefined);
    this.currentUser = new BehaviorSubject(undefined);
    this.loginUserToken.next(this.getTokenFromLocalStorege());
    if (this.loginUserToken.value != null) {
      this.getUserFromToken();
    }
  }
  /**
   * getLoginUser
   */
  public getLoginUserAsObservable() {
    return this.currentUser.asObservable();
  }
  public getLoginUserTokenAsObservable() {
    return this.loginUserToken.asObservable();
  }
  public async login(user: User): Promise<any> {
    // tslint:disable-next-line:no-shadowed-variable
    return new Promise<any>(async (resolve: any, reject: any) => {
      try {
        const result: any = await this.http.post(`${environment.server}/api/auth/login`, user).toPromise();
        if (result.massageCode === 1) {
          reject('bedUsername');
        } else if (result.massageCode === 2) {
          reject('bed password');
        } else {
          this.loginUserToken.next(result.token);
          this.getUserFromToken();
          this.saveTokenToLocalStorege(result.token);
          resolve();
        }
      } catch (error) {
        reject('error');
      }
    });
  }
  public getUserFromToken(): void {
    const headers = new HttpHeaders({
      'x-access-token': this.loginUserToken.value
    });
    this.http.get(`${environment.server}/api/auth/userFromToken`, { headers }).toPromise()
      .then((data: User) => {
        this.currentUser.next(data);
      })
      .catch((error) => {
        console.log(error);
      });
  }
  public isLogin(): Promise<boolean> {
    return new Promise((resolve, reject) => {
      this.currentUser.asObservable()
      .subscribe((data) => {
        if (data) {
          resolve(true);
        } else {
          resolve(false);
        }
      }).unsubscribe();
    });
  }
  public saveTokenToLocalStorege(token: string): void {
    localStorage.setItem('chanToken', token);
  }
  public getTokenFromLocalStorege(): string {
    return localStorage.getItem('chanToken');
  }
  public removeTokenFromLocalStrege(): void {
    localStorage.removeItem('chanToken');
  }
}

auth.guaed.ts:

import { Injectable } from '@angular/core';
import { CanActivate, CanLoad, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from '../auth.service';
import { Route } from '@angular/compiler/src/core';
import { last, map } from 'rxjs/operators';
@Injectable()
export class AuthGuard implements CanLoad {

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

  canLoad(route: Route): boolean | Observable<boolean> | Promise<boolean> {
    console.log('use gruad');
    return this.authSerivce.isLogin();
  }
}

【问题讨论】:

  • 你明确地创建了一个未定义的行为主体。如果在 API 响应之前没有合理的值,不要给出默认值。此外,您应该真正意识到您可以将 observables 直接转换为 Promise,反之亦然,所有的包装都是不必要的。
  • 试图删除未定义的结果并报错,它需要一个初始值。

标签: angular rxjs angular2-routing angular6


【解决方案1】:

那是因为 BehaviourSubject 的工作原理。当您将Subscribe 转换为BehaviourSubject 时,它会立即返回最后一个值。这就是为什么 BehaviourSubject 需要一个默认值。

在您的情况下,一旦AuthGuard 被激活,它将调用您的方法isLogin,该方法订阅currentUser,并将返回undefined(取决于javascript 执行的顺序)。基本上你的AuthGuard 不会等待你的函数getUserFromToken() 完成。

当您使用ReplaySubject 而不是BehaviourSubject 时,您可以轻松解决此解决方案。

private currentUser: ReplaySubject&lt;User&gt; = new ReplaySubject(1)

ReplaySubject初始化为1,它会缓存你当前的用户,但是当没有用户存在时不会触发。

目前,您的 Authguard 将等待有效的 currentUser 值,并且在 currentUser 未定义时不会触发

【讨论】:

    猜你喜欢
    • 2020-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-07
    • 2020-02-04
    • 2022-10-30
    • 1970-01-01
    • 2017-03-10
    相关资源
    最近更新 更多