Angular AuthGuard - 按顺序检查身份验证和数据库条目
Angular AuthGuard - Checking Authentication and Database Entry Sequentially
要允许访问管理路由,我必须检查两件事:
- 如果用户已通过身份验证
- 如果这个用户是管理员。我从 firebase 数据库获取管理状态。
AdminGuard
import {ActivatedRouteSnapshot, CanActivate, Router,
RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs/Observable';
import {AngularFireAuth} from 'angularfire2/auth';
import {Injectable} from '@angular/core';
import {DbActionsService} from '../services/db-actions.service';
import {AuthService} from '../services/auth.service';
import 'rxjs/add/operator/map';
@Injectable()
export class AdminGuard implements CanActivate {
constructor(private afAuth: AngularFireAuth, private router: Router, private dbAction: DbActionsService, private authService : AuthService) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
if (this.afAuth.auth.currentUser) {
return this.dbAction.getUserProfileData(this.afAuth.auth.currentUser.email).map((user) => {
if (user[0].admin) {
return true;
} else {
this.router.navigate(['/']);
return false;
}
}).take(1)
} else {
this.router.navigate(['/']);
}
}
}
服务功能
getUserProfileData(userEmail: string) {
this.dataRef = this.afDatabase.list('data/users', ref => ref.orderByChild('profile/email').equalTo(userEmail));
this.data = this.dataRef.snapshotChanges().map(changes => {
return changes.map(c => ({ key: c.payload.key, ...c.payload.val() }));
});
return this.data;
}
这工作正常,但是,我的主要问题是当我刷新(或最初加载)具有 AdminGuard 的页面时,它总是将我重定向到主页,因为 AdminGuard 不等待身份验证响应。
我试过的
一个新的 AuthService
import { Injectable } from '@angular/core';
import {AngularFireAuth} from 'angularfire2/auth';
import {Observable} from 'rxjs/Observable';
@Injectable()
export class AuthService {
private user: any;
constructor(private afAuth: AngularFireAuth) { }
setUser(user) {
this.user = user;
}
getAuthenticated(): Observable<any> {
return this.afAuth.authState;
}
}
新 AdminGuard
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs/Observable';
import {AngularFireAuth} from 'angularfire2/auth';
import {Injectable} from '@angular/core';
import {DbActionsService} from '../services/db-actions.service';
import {AuthService} from '../services/auth.service';
import 'rxjs/add/operator/map';
@Injectable()
export class AdminGuard implements CanActivate {
constructor(private afAuth: AngularFireAuth, private router: Router, private dbAction: DbActionsService, private authService : AuthService) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
return this.authService.getAuthenticated().map(user => {
this.authService.setUser(user);
return user ? true : false;
});
}
}
这在初始加载时与 Auth Check 一起工作,但我如何实现数据库条件来检查用户是否是管理员?我不知道...
这是一个基于你的代码应该工作的守卫,让我们分解一下:
- 让用户
authState
可观察。
- 输入
switchMap
以切换到您想要的数据库数组。
- 将 admin 属性 映射为带有双响的布尔值。
- 使用点击处理重定向。
import { tap, map, switchMap, take } from 'rxjs/operators;
// ...omitted
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
return this.afAuth.authState.pipe(
take(1),
switchMap(user => {
return this.dbAction.getUserProfileData(user.email)
}),
map(profile => !!(profile.length && profile[0].admin),
tap(isAdmin => {
if (isAdmin) {
console.log('admin user, you shall pass')
} else {
console.log('non-admin user, go home')
this.router.navigate(['/']);
}
})
)
}
我已经用 Angular/Firebase 完成了许多守卫实现,并且对什么最有效有一些看法 - 我认为这个 role based auth screencast 可能会帮助您改进当前的方法
要允许访问管理路由,我必须检查两件事:
- 如果用户已通过身份验证
- 如果这个用户是管理员。我从 firebase 数据库获取管理状态。
AdminGuard
import {ActivatedRouteSnapshot, CanActivate, Router,
RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs/Observable';
import {AngularFireAuth} from 'angularfire2/auth';
import {Injectable} from '@angular/core';
import {DbActionsService} from '../services/db-actions.service';
import {AuthService} from '../services/auth.service';
import 'rxjs/add/operator/map';
@Injectable()
export class AdminGuard implements CanActivate {
constructor(private afAuth: AngularFireAuth, private router: Router, private dbAction: DbActionsService, private authService : AuthService) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
if (this.afAuth.auth.currentUser) {
return this.dbAction.getUserProfileData(this.afAuth.auth.currentUser.email).map((user) => {
if (user[0].admin) {
return true;
} else {
this.router.navigate(['/']);
return false;
}
}).take(1)
} else {
this.router.navigate(['/']);
}
}
}
服务功能
getUserProfileData(userEmail: string) {
this.dataRef = this.afDatabase.list('data/users', ref => ref.orderByChild('profile/email').equalTo(userEmail));
this.data = this.dataRef.snapshotChanges().map(changes => {
return changes.map(c => ({ key: c.payload.key, ...c.payload.val() }));
});
return this.data;
}
这工作正常,但是,我的主要问题是当我刷新(或最初加载)具有 AdminGuard 的页面时,它总是将我重定向到主页,因为 AdminGuard 不等待身份验证响应。
我试过的
一个新的 AuthService
import { Injectable } from '@angular/core';
import {AngularFireAuth} from 'angularfire2/auth';
import {Observable} from 'rxjs/Observable';
@Injectable()
export class AuthService {
private user: any;
constructor(private afAuth: AngularFireAuth) { }
setUser(user) {
this.user = user;
}
getAuthenticated(): Observable<any> {
return this.afAuth.authState;
}
}
新 AdminGuard
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs/Observable';
import {AngularFireAuth} from 'angularfire2/auth';
import {Injectable} from '@angular/core';
import {DbActionsService} from '../services/db-actions.service';
import {AuthService} from '../services/auth.service';
import 'rxjs/add/operator/map';
@Injectable()
export class AdminGuard implements CanActivate {
constructor(private afAuth: AngularFireAuth, private router: Router, private dbAction: DbActionsService, private authService : AuthService) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
return this.authService.getAuthenticated().map(user => {
this.authService.setUser(user);
return user ? true : false;
});
}
}
这在初始加载时与 Auth Check 一起工作,但我如何实现数据库条件来检查用户是否是管理员?我不知道...
这是一个基于你的代码应该工作的守卫,让我们分解一下:
- 让用户
authState
可观察。 - 输入
switchMap
以切换到您想要的数据库数组。 - 将 admin 属性 映射为带有双响的布尔值。
- 使用点击处理重定向。
import { tap, map, switchMap, take } from 'rxjs/operators;
// ...omitted
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
return this.afAuth.authState.pipe(
take(1),
switchMap(user => {
return this.dbAction.getUserProfileData(user.email)
}),
map(profile => !!(profile.length && profile[0].admin),
tap(isAdmin => {
if (isAdmin) {
console.log('admin user, you shall pass')
} else {
console.log('non-admin user, go home')
this.router.navigate(['/']);
}
})
)
}
我已经用 Angular/Firebase 完成了许多守卫实现,并且对什么最有效有一些看法 - 我认为这个 role based auth screencast 可能会帮助您改进当前的方法