Angular 带有auth.service的路由保护

Angular 带有auth.service的路由保护,angular,rxjs,angular2-routing,angular6,Angular,Rxjs,Angular2 Routing,Angular6,我正在尝试创建一个路由保护,它可以保护一些延迟加载的模块的安全,我有一个带有BehaviorSubject的auth服务,它保存当前用户和JWT令牌 当我调用guard时,它首先获取当前用户的默认值,并且仅在第二次尝试时才允许用户到达路由 auth.service.ts: import { Injectable } from '@angular/core'; import { HttpClient, HttpErrorResponse, HttpResponse, HttpHeaders } f

我正在尝试创建一个路由保护,它可以保护一些延迟加载的模块的安全,我有一个带有BehaviorSubject的auth服务,它保存当前用户和JWT令牌

当我调用guard时,它首先获取当前用户的默认值,并且仅在第二次尝试时才允许用户到达路由

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');
  }
}
从'@angular/core'导入{Injectable};
从“@angular/common/http”导入{HttpClient,HttpErrorResponse,HttpResponse,HttpHeaders};
从“rxjs”导入{observeable,BehaviorSubject};
从'rxjs/operators'导入{map,skip,takeLast,last};
从“../shared/User”导入{User};
从“../../environments/environment”导入{environment};
@可注射()
导出类身份验证服务{
私人登录主题:行为主体;
private currentUser:行为主体;
构造函数(专用http:HttpClient){
//服务初始化
this.loginUserToken=新行为主体(未定义);
this.currentUser=新行为主体(未定义);
this.loginUserToken.next(this.getTokenFromLocalStorege());
if(this.loginUserToken.value!=null){
这个.getUserFromToken();
}
}
/**
*getLoginUser
*/
公共getLoginUsersObservable(){
返回此.currentUser.asObservable();
}
公共getLoginUserTokenAsObservable(){
返回此.loginUserToken.asObservable();
}
公共异步登录(用户:用户):承诺{
//tslint:禁用下一行:无阴影变量
返回新承诺(异步(解析:任意,拒绝:任意)=>{
试一试{
const result:any=wait this.http.post(`${environment.server}/api/auth/login`,user.toPromise();
if(result.massageCode==1){
拒绝(“bedUsername”);
}else if(result.massageCode==2){
拒绝(“床位密码”);
}否则{
this.loginUserToken.next(result.token);
这个.getUserFromToken();
这个.saveTokenToLocalStorege(result.token);
解决();
}
}捕获(错误){
拒绝(“错误”);
}
});
}
public getUserFromToken():void{
const headers=新的HttpHeaders({
“x-access-token”:this.loginUserToken.value
});
this.http.get(`${environment.server}/api/auth/userFromToken`,{headers}).toPromise()
。然后((数据:用户)=>{
this.currentUser.next(数据);
})
.catch((错误)=>{
console.log(错误);
});
}
public isLogin():承诺{
返回新承诺((解决、拒绝)=>{
this.currentUser.asObservable()
.订阅((数据)=>{
如果(数据){
决心(正确);
}否则{
决议(假);
}
}).取消订阅();
});
}
public saveTokenToLocalStorege(标记:字符串):void{
setItem('chanToken',标记);
}
public getTokenFromLocalStorege():字符串{
返回localStorage.getItem('chanToken');
}
public removeTokenFromLocalStrege():void{
localStorage.removietem('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();
  }
}
从'@angular/core'导入{Injectable};
从'@angular/Router'导入{CanActivate,CanLoad,ActivatedRouteSnapshot,RouterStateSnashot,Router};
从“rxjs”导入{Observable};
从“../auth.service”导入{AuthService};
从'@angular/compiler/src/core'导入{Route};
从“rxjs/operators”导入{last,map};
@可注射()
导出类AuthGuard实现CanLoad{
构造函数(私有AuthService:AuthService,私有路由器:路由器){}
canLoad(路径:路径):布尔值|可观察|承诺{
log('use gruad');
返回这个.authSerivce.isLogin();
}
}

这是因为
行为主体是如何工作的。当您订阅
行为主题时,它会立即返回最后一个值。这就是
行为主题
需要默认值的原因

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

当您使用
ReplaySubject
而不是
Behavior Subject
时,可以轻松解决此问题

private currentUser:ReplaySubject=新的ReplaySubject(1)

使用值1初始化
ReplaySubject
,它将缓存当前用户,但在不存在用户时不会触发


目前,您的
Authguard
将等待有效的
currentUser
值,并且当
currentUser
未定义时,将不会激发
。如果在API响应之前没有合理的值,不要给出默认值。此外,您还应该意识到,您可以直接将可观察结果转换为承诺,反之亦然,所有包装都是不必要的。如果要删除带有错误的未定义结果,则需要具有初始值。