Angular 10 行为主体 returns 调用 .next(value) 方法后为空

Angular 10 behavior subject returns null after calling .next(value) method

我一直在绞尽脑汁想弄清楚是什么原因导致登录后我的用户值为空。

这是我的登录页面。它调用 api 并得到如下所示的结果:

这是我的身份验证服务:

export class AuthenticationService {
  private userSubject: BehaviorSubject<User>;
  public user: Observable<User>;

  constructor(
    private router: Router,
    private http: HttpClient
  ) {
    this.userSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('user')));
    this.user = this.userSubject.asObservable();
  }
  public get userValue(): User {
    return this.userSubject.value;
  }

  GetUsername(): string {
    if (localStorage.getItem('currentUser') != null)
      return JSON.parse(localStorage.getItem('currentUser')).UserName;
    return null;
  }
  login(username: string, password: string) {

    return this.http.post<any>(`${Statics.ApiUrl}users/authenticate`, { Username: username, Password: password }, { responseType: "json" })
      .pipe(map(user => {
        debugger;
        localStorage.setItem('user', JSON.stringify(user));
        this.userSubject.next(user);
        return user;
      }));
  }

  logout() {
    //remove user from local storage to log user out
    localStorage.removeItem('user');
    this.userSubject.next(null);
    this.router.navigate(['/login']);
  }

  signUp(RegisterModel: RegisterModel): Observable<User> {
    return this.http.post<User>(`${Statics.ApiUrl}users/signup`, RegisterModel, { responseType: "json" });
  }
  verifyUserPhone(username: string, password: string, verificationCode: string): Observable<User> {
    return this.http.put<User>(Statics.ApiUrl + 'users/verifyUserPhone', { username, password, verificationCode }, { responseType: "json" });
  }
}

这是我的授权人:

export class AuthGuard implements CanActivate {
    constructor(
        private router: Router,
        private authenticationService: AuthenticationService
    ) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
      debugger;
      const user = this.authenticationService.userValue;
      var token_expiration=new Date(user?.TokenExpirartion);
      var now=new Date();
        if (user && token_expiration>now) {
            // check if route is restricted by role
            if (route.data.roles && route.data.roles.indexOf(user.role) === -1) {
                // role not authorised so redirect to home page
                this.router.navigate(['/']);
                return false;
            }

            // authorized so return true
            return true;
        }

        // not logged in so redirect to login page with the return url
        this.authenticationService.logout();
        this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
        return false;
    }
}

我登录并获取用户数据并将其存储在本地存储中,但我的 authguard 从不激活路由,因为身份验证服务总是 return 用户 NULL.If 然后我只刷新页面有用。至于注销,它不会注销用户,直到我刷新页面。

见下文:

这是我的登录按钮 ts 代码:

this.AuthenticationService.login(this.username, this.password)
  .pipe(first())
  .subscribe({
    next: () => {

      // get return url from query parameters or default to home page
      const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
      this.router.navigateByUrl(returnUrl);
    },
    error: error => {
      this.error = error;
      this.submited = false;
    }
  });

当 authguard 在登录后检查 userSubject 时 returns null。无论我登录多少次,除非我刷新页面,否则它将 return 为空。 只有这样 userSubject 才会被评估。我不知道该怎么办。我无法登录我的网站。我必须登录然后刷新页面才能使 authguard 工作。

刷新页面后我得到值:

似乎授权服务需要一些时间来获取数据,到那时路由已被拒绝,因为它为空。

您可以尝试在 approving/rejecting 路线

之前添加一些等待

你们的服务是在 root 中提供的吗?如果没有,您可能会得到多个实例。

@Injectable({
  providedIn: 'root'
})
export class AuthenticationService {
...

这里的问题是您的 authguard 是同步的,并且在实际加载用户之前调用了 canActivate。如果您的 canActivate 将 return observable of boolean,而不是 boolean,您可以解决此问题。 首先,您需要添加函数,如果已经加载,用户将 return 可观察到该函数,并加载用户观察,如下所示:

public getOrFetchUser(): Observable<User> {
   // please note that we return observable in both cases
   return this.userSubject.value ? of(this.userSubject.value) : this.login();
}

然后将您的 canActivate 守卫更改为:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.authenticationService.getOrFetchUser().pipe(
   map((user) => {
      console.log('user, ', user);
      // change return to your validation logic
      return true; 
});