1

我遇到了一个有趣的问题,当我尝试重定向时,当用户尝试访问他们不应该访问的路由时,我的 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;
                    }
                }
            })
        );
    }

}

身份验证服务

// 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');
        }
    }
}

应用程序 从下面的屏幕截图中可以看到,日志是正确的,我们应该被重定向到仪表板,但登录组件和路由仍然处于激活状态。 包含控制台日志并显示登录路径仍处于活动状态的屏幕截图

4

2 回答 2

1

Angular Guard 应该使用布尔值解析。因此,不要使用点击运算符,而是使用地图运算符并始终返回布尔值,即使您正在重定向。尝试将该代码更改为以下内容:

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;
                    }
                }
            })
        );

更新:从 v7.1 开始,守卫应该解析为布尔值或 UrlTree 对象。如果返回 UrlTree 对象,则守卫将路由到该 Url。Router.parseUrlRouter.createUrlTree方法可用于创建 UrlTree 对象。

    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');
                        return this._router.parseUrl('/login');
                    }
                }
                // authenticated
                else {
                    // accessing sign in or sign up pages
                    if (['/login', '/register'].includes(url)) {
                        console.log('Should bounce to dashboard');
                        this._router.parseUrl('/dashboard');
                    }
                    // accessing application
                    else {
                        console.log('Allow through');
                        return true;
                    }
                }
            })
        );
于 2019-07-19T15:28:11.397 回答
0

最近我遇到了同样的问题,结果发现我在我一直重定向到的路线上设置了另一个守卫。因此,请确保您没有任何其他警卫login

于 2019-07-19T15:48:47.143 回答