使用Observable时,无法从Angular Guard进行导航

时间:2019-07-19 15:12:35

标签: angular rxjs6 angular-guards

我遇到了一个有趣的问题,当用户尝试访问他们不应该访问的路由时,当我尝试重定向时,Angular Guard似乎什么也没做。

如您所见,我在整个过程中都放置了console.log调用,并且看到了我期望的消息,但是导航从未发生,并且最初请求的页面也像平常一样加载。这真的让我感到困惑-任何帮助将不胜感激!

警卫队

// angular dependencies
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';

// rxjs utils
import { Observable } from 'rxjs';
import { take, map, tap } from 'rxjs/operators';

// service(s)
import { AuthService } from './services/auth/auth.service';

// interface(s)
import User from './interfaces/user.interface';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {

    constructor(private _authService: AuthService, private _router: Router) { }

    canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
        const url = state.url;
        console.log('Auth Guard hit', { url: url });

        return this._authService.user.pipe(
            take(1),
            map(user => {
                console.log(user);
                return !!user;
            }),
            tap(authenticated => {
                console.log(`Authenticated: ${authenticated}`);
                // not authenticated
                if (!authenticated) {
                    // accessing sign in or sign up pages
                    if (['/login', '/register'].includes(url)) {
                        console.log('Allow through');
                        return true;
                    }
                    // accessing application
                    else {
                        console.log('Should bounce to login');
                        return this._router.createUrlTree(['/login']);
                    }
                }
                // authenticated
                else {
                    // accessing sign in or sign up pages
                    if (['/login', '/register'].includes(url)) {
                        console.log('Should bounce to dashboard');
                        return this._router.createUrlTree(['/dashboard']);
                    }
                    // accessing application
                    else {
                        console.log('Allow through');
                        return true;
                    }
                }
            })
        );
    }

}

AuthService

// angular dependencies
import { Injectable } from '@angular/core';

// firebase dependencies
import { AngularFireAuth } from '@angular/fire/auth';
import { AngularFirestore } from '@angular/fire/firestore';

// RXJS helpers
import { Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';

// interfaces
import User from '../../interfaces/user.interface';

@Injectable({ providedIn: 'root' })
export class AuthService {
    public user: Observable<User> = null;

    constructor(private _auth: AngularFireAuth, private _firestore: AngularFirestore) {
        this.user = this._auth.authState.pipe(
            switchMap(({ uid }) => {
                if (uid) {
                    return this._firestore.doc(`users/${uid}`).valueChanges();
                } else {
                    return of(null);
                }
            })
        );
    }

    public async createAccount(email: string, password: string, forename: string, surname: string, relevantTags: string[] = []) {
        // create the user in the auth system
        let authUser;
        try {
            authUser = await this._auth.auth.createUserWithEmailAndPassword(email, password);
        } catch (error) {
            console.error('Failed to create user in auth system', { reason: error.message });
            throw error;
        }

        // flesh out the user data
        const data: User = {
            uid: authUser.user.uid,
            forename: forename,
            surname: surname,
            displayName: `${forename} ${surname}`,
            roles: ['user'],   // everyone has user role, can be promoted by admin as required
            relevantTags: relevantTags,
            emailVerified: false
        };

        // create the user in the database
        try {
            this._firestore.doc(`users/${data.uid}`).set(data);
        } catch (error) {
            console.error('Failed to create user in database', { reason: error.message });
            throw error;
        }

        // attempt to send the initial verification email
        try {
            this.sendVerification();
        } catch (error) {
            console.warn('Failed to send verification email', { reason: error.message });
            throw error;
        }
    }

    public async signIn(email: string, password: string) {
        // attempt to sign in
        let result: firebase.auth.UserCredential;
        try {
            result = await this._auth.auth.signInWithEmailAndPassword(email, password);
        } catch (error) {
            console.error('Failed to log in', { reason: error.message });
            throw error;
        }

        // store the user data for access by controllers
        try {
            this.user = this._firestore.doc<User>(`users/${result.user.uid}`).valueChanges();
        } catch (error) {
            console.error('Failed to set user on service', { reason: error.message });
            this._auth.auth.signOut();
            throw new Error('Failed to log in');
        }
    }

    public async signOut() {
        // attempt to sign out
        try {
            await this._auth.auth.signOut();
        } catch (error) {
            console.error('Failed to log out', { reason: error.message });
            throw new Error('Failed to log out');
        }
    }

    public async sendVerification() {
        // attempt to send verification email
        try {
            this._auth.auth.currentUser.sendEmailVerification();
        } catch (error) {
            console.error('Failed to send verification', { reason: error.message });
            throw new Error('Failed to send verification');
        }
    }
}

应用 从下面的屏幕快照中可以看到,日志是正确的,我们应该将它们重定向到仪表板,但登录组件和路由仍处于激活状态。 Screenshot containing the console logs and showing the login route is still active

2 个答案:

答案 0 :(得分:1)

Angular Guard应该以布尔值解析。因此,即使使用重定向,也不要使用tap运算符,而要使用map运算符并始终返回一个布尔值。尝试将代码更改为以下代码:

return this._authService.user.pipe(
            take(1),
            map(user => {
                console.log(user);
                return !!user;
            }),
            map(authenticated => {
                console.log(`Authenticated: ${authenticated}`);
                // not authenticated
                if (!authenticated) {
                    // accessing sign in or sign up pages
                    if (['/login', '/register'].includes(url)) {
                        console.log('Allow through');
                        return true;
                    }
                    // accessing application
                    else {
                        console.log('Should bounce to login');
                        this._router.navigateByUrl('/login');
                        return false;
                    }
                }
                // authenticated
                else {
                    // accessing sign in or sign up pages
                    if (['/login', '/register'].includes(url)) {
                        console.log('Should bounce to dashboard');
                        this._router.navigateByUrl('/dashboard');
                        return false;
                    }
                    // accessing application
                    else {
                        console.log('Allow through');
                        return true;
                    }
                }
            })
        );

答案 1 :(得分:0)

最近我遇到了同样的问题,结果是我在重定向到的路由上设置了另一个防护。因此,请确保您在login

上没有其他警卫