【问题标题】:Authentication Problem - Order of execution of Observables and Promises (Angular / REST)身份验证问题 - Observables 和 Promise 的执行顺序(Angular / REST)
【发布时间】:2019-10-25 21:53:34
【问题描述】:

我使用 Angular 7。

我是 Angular 新手,在我工作的机构,我们将创建一个新的 Angular 应用程序。

我的一位同事创建了后端(Restful),我从身份验证功能开始。 所以首先我们有一个带有顶栏和侧边栏的布局,它们在每个屏幕上。

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

我添加了一个具有登录、注销和 isAuthenticated 功能的 AuthenticationService。

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 的提交表单中调用了 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 获取用户的时间太长,并且之前导航到“/dashboard”。 之后,AuthGuard 试图获取 AuthenticationService 的 currentUser,但它仍然不存在,所以我被踢回“/login”并不得不再次登录。 在我被踢到“/login”之后,在 AuthGuard 服务中设置了 currentUser,但现在为时已晚,我无法订阅它,因为它在第一次调用时未定义。

我尝试在AuthenticationService的登录函数中获取管道内的用户,但没有成功。

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

我导航到 /dashboard,在顶部栏中我可以看到我的用户名。

但是在隐藏状态下,AuthGuard 应该观察用户是否通过了身份验证,如果没有导航到 /login。

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

我希望你们能理解我的意思,如果不请自来。 如果需要,我可以附加一些代码行或显示其他组件。

【问题讨论】:

    标签: php angular typescript authentication jwt


    【解决方案1】:

    您使用 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));
      }
    

    【讨论】:

    • 在 AuthenticationService 中设置 currentUser 变量的最佳方法是什么?如果我以这种方式定义变量,则可能不会定义 currentUserSubject。
    • this.authenticationService.currentUserSubject.next(user); 不要直接更新curentUser 变量。
    • currentUserSubject 必须在 AuthenticationService 构造函数中定义:readonly currentUserSubject = new BehaviorSubject&lt;User&gt;(null)。
    • 好吧,这似乎可行。我被重定向到/dashboard,但重新加载后我必须再次登录。
    • 这是另一个故事。您必须从 localStore 获取令牌,在初始化 Angular 应用程序之前请求User。 APP_INITIALIZER 是解决这个问题的关键字。
    猜你喜欢
    • 1970-01-01
    • 2021-05-12
    • 2017-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-22
    • 1970-01-01
    • 2013-06-16
    相关资源
    最近更新 更多