身份验证问题-可观察对象和承诺的执行顺序(角度/ REST)

时间:2019-06-11 12:58:35

标签: php angular typescript authentication jwt

我使用Angular 7。

我对Angle并不陌生,在我工作的代理商中,我们将创建一个新的angular应用。

我的一个同事创建了后端(Restful),然后我开始使用身份验证功能。 因此,首先,我们在每个屏幕上都有一个带有顶部栏和侧栏的布局。

完成布局后,我从登录组件开始,还创建了一个JWT拦截器,将令牌附加到任何标头。 并创建了AuthGuard来保护大多数路径。

我添加了一个AuthenticationService,其功能为登录,注销和isAuthenticated。

export class AuthenticationService {
  public currentUserSubject: BehaviorSubject<User>;
  public currentUser: Observable<User>;
  returnUrl: string;

  constructor(private http: HttpClient, private userService: UserService,  private router: Router, private route: ActivatedRoute) {
    if (localStorage.getItem("currentUser")) {
      const userId = JSON.parse(localStorage.getItem("currentUser")).id;
      userService.getById(userId).subscribe((data) => {
        this.currentUserSubject = new BehaviorSubject<User>(data as User);
        this.currentUser = this.currentUserSubject.asObservable();
      });
    }
  }

  isAuthenticated() {
    const authState = new Subject<boolean>();

    this.currentUser.subscribe(data => {
      console.log(data);
      if (data) {
        authState.next(true);
      } else {
        authState.next(false);
      }
    });

    return authState.asObservable();
  }

  login(email: string, password: string) {
    return this.http.post<any>(`http://pitchbook.localhost/authentication_token`, {
      username: email,
      password
    })
        .pipe(map(object => {
          // login successful if there's a jwt token in the response
          if (object.user && object.token) {
            this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';

            const jsonObject = {
              id: object.user.id,
              token: object.token
            };

            // store user details and jwt token in local storage to keep user logged in between page refreshes
            localStorage.setItem('currentUser', JSON.stringify(jsonObject));

            return jsonObject;
          }

          return object;
        }));
  }

  logout() {
    // remove user from local storage to log user out
    localStorage.removeItem('currentUser');
    this.currentUserSubject.next(null);
    location.reload(true);
  }
}

然后我以LoginComponent的Submit形式调用了login()。

  onSubmit() {
    // stop here if form is invalid
    if (this.loginForm.invalid) {
      return;
    }

    this.loading = true;
    this.authenticationService.login(this.loginF.email.value, this.loginF.password.value)
        .pipe(first()).subscribe(
        (object) => {
          this.userService.getById(object.id as number).subscribe((data) => {
            this.authenticationService.currentUserSubject.next(data as User);
            this.authenticationService.currentUser = this.authenticationService.currentUserSubject.asObservable();

            console.log(this.authenticationService.currentUser);

            this.router.navigate(["/dashboard"]);
            this.alertService.success("Success");
          });
        },
        (error) => {
          this.loading = false;
          this.alertService.error("Error");
        }
    );
  }

所以现在的问题是,当我登录ID为ID的User时,获取时间太长了,之前导航到“ / dashboard”就发生了。 之后,AuthGuard试图获取AuthenticationService的currentUser,但它仍然不存在,因此我被踢回了“ / login”并不得不再次登录。 在我被踢到“ /登录”之后,在AuthGuard服务中设置了currentUser,但是现在为时已晚,我无法订阅它,因为它在第一次调用时是未定义的。

我试图通过AuthenticationService的登录功能将User放入管道中,但是没有用。

我期望的结果是,我按下登录按钮,设置了currentUser(从具有ID的API中获取用户(因为用户对象很大,并且不应完全响应身份验证请求))

我转到/ dashboard,然后在顶部栏中看到我的用户名。

但是在隐藏状态下,如果未导航到/ login,则AuthGuard应该监视用户是否已通过身份验证。

页面刷新后,我想保持登录状态

我希望你们能理解我的意思,即使不是随意问。 如果需要,我可以添加一些代码行或显示其他组件。

1 个答案:

答案 0 :(得分:0)

使用RxJS的方式存在几个问题。您不应以这种方式覆盖Observables变量。

AuthenticationService中以这种方式声明currentUser

  public readonly currentUser = this.currentUserSubject.asObservable();

然后从onSubmit()方法中删除以下内容。

this.authenticationService.currentUser = this.authenticationService.currentUserSubject.asObservable();

isAuthenticated方法编写错误(泄漏订阅),一种更好的方法可能是:

  isAuthenticated() {
    return this.currentUser.pipe(map(user => !!user));
  }