【问题标题】:Need Angular 8 canActivate to return Observable<boolean> of a Subject需要 Angular 8 canActivate 来返回一个 Subject 的 Observable<boolean>
【发布时间】:2021-08-02 16:08:14
【问题描述】:

所以我有一个使用 cookie 处理身份验证的 Angular 8 应用程序(使用 Laravel Sanctum 的 Laravel 服务器)。因为我使用的是 cookie,并且我没有在 localStorage 中存储任何用户数据/令牌,所以每次我的应用程序使用存储的 cookie 初始化时,我都必须从服务器获取当前用户。

auth.service.ts

export class AuthService {

    public readonly INITIAL_PATH = '/app/dashboard';
    public readonly LOGIN_PATH = '/login';

    private loggedUserSubject: Subject<User>;
    public loggedUser: Observable<User>;

    constructor(
        private apiService: ApiService,
        private http: HttpClient,
        private router: Router,
        private csrfService: CsrfService,
    ) {
        this.loggedUserSubject = new Subject;
        this.loggedUser = this.loggedUserSubject.asObservable();

        this.getCurrentUser().subscribe(); // Initial call to server to get current user
    }

    ...

    getCurrentUser(): Observable<User> {
        return this.http.get<User>(`${config.authUrl}/user`)
            .pipe(
                tap(user => {
                    this.loggedUserSubject.next(user);
                }),
                share()
            );
    }

    isLoggedIn(): Observable<boolean> {
        return this.loggedUser.pipe(
            tap(user => {
                console.log('logged user: ', user);
            }),
            map(user => !!user),
            catchError(() => of(false)),
        )
    }

    ...
}

auth.guard.ts

export class AuthGuard implements CanActivate {
    constructor(
        private _router: Router,
        private _authService: AuthService
    ) { }

    canActivate(): Observable<boolean> {
        return this._authService.isLoggedIn().pipe(
            tap(isLoggedIn => {
                if (isLoggedIn) {
                    this._router.navigate(['/app/dashboard']);
                }
            }),
            map(isLoggedIn => !isLoggedIn)
        );
    }
}

每当我的 AuthGuard 被击中时,它都必须调用 AuthService.isLoggedIn() 来返回一个 Observable&lt;boolean&gt; 来判断用户是否在内存中。问题是,我返回的 Subject 可能已经发出也可能没有发出一个值,因为我必须向我的服务器发出异步请求以最初获取当前用户。我不能使用 BehaviourSubject 因为 canActivate 返回第一个发出的值...直到服务器响应我才知道。

如何设置以使 AuthGuard.canActivate() 等待我的服务提供经过身份验证的用户?

【问题讨论】:

  • 尝试更改保护以返回布尔值。 AFAIK,如果您返回 Observable,它会返回 Observable 并且不会等待 Observable 完成。如果返回布尔值,它会自动等待 Observable 完成,以便返回布尔值。

标签: javascript angular http rxjs


【解决方案1】:

我想通了。首先,我将loggedUserSubject 从一个Subject 更改为一个初始值为undefined 的BehaviorSubject(我不知道这是否对TBH 有任何影响)。然后我必须添加一个过滤器运算符 isLoggedIn() Observable 以仅返回第一个 defined 值(如果已登录,则为用户,如果未登录,则为 null)。否则返回第一个发出的值,该值之前未定义。

auth.service.ts

isLoggedIn(): Observable<boolean> {
    return this.auth.getCurrentUser().pipe(
        filter(user => user !== undefined),
        map(user => {
            return !!user;
        }),
    );
}

auth.guard.ts

canActivate(): Observable<boolean> {
    return this._authService.isLoggedIn().pipe(
        first(),
        tap(isLoggedIn => {
            if (isLoggedIn) {
                this._router.navigate(['/app/dashboard']);
            }
        }),
        map(isLoggedIn => !isLoggedIn)
    );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-25
    • 1970-01-01
    • 1970-01-01
    • 2019-04-30
    • 2021-11-14
    • 2019-08-03
    相关资源
    最近更新 更多