我正在尝试在Angular 4.2.4中为管理路由实现canActivate防护。
根据这个堆栈问题:canActivate Question,我认为我正确地做了一切。但是,唉,我似乎无法让事情发挥作用。
以下是模块守卫:
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private _store: Store<AppState>,
private router: Router,
private czauth: CzauthService) { }
canActivate(route: ActivatedRouteSnapshot):Observable<boolean>{
return this.czauth.verifyAdminUser().first();
}
};
以下是我的czauth服务:
getToken():Observable<firebase.User>{
return this.af.idToken;
}
verifyUserRole(token){
this.token = token;
console.log(token);// Log out "PromiseObservable {_isScalar: false, promise: D, scheduler: undefined}"
let headers = new Headers({ 'Authorization': 'Bearer ' + token});
let options = new RequestOptions({ headers: headers });
return this.http.get(environment.api+'/verifyadminuser', options).map((response)=>{return response.json()});
}
verifyAdminUser():Observable<boolean>{
return this.getToken()
.map((user)=>{return Observable.fromPromise(user.getIdToken())})
.map((token)=>{return this.verifyUserRole(token)})
.do((response)=>{console.log(response)})// Always returns "Observable {_isScalar: false, source: Observable, operator: MapOperator}"
.switchMap((response:any)=>{ return response.status === 200 ? Observable.of(true) : Observable.of(false)});
}
我永远无法从我的http异步调用中获得响应。我总是在控制台中看到看起来很冷的东西。好像路由器永远不会订阅我的observable一样?我想根据我的http响应返回一个布尔值。我怎样才能做到这一点?
编辑:
我从警卫处调用verifyAdminUser
方法。该服务是根模块上的一个sinlgeton。警卫正在保护对延迟加载模块的访问。
下面我列出了我在路由中使用防护的地方。
路由模块:
const routes: Routes = [
{path: '', loadChildren : './landing/landing.module#LandingModule'},
{path: 'dashboard', loadChildren : './dashboard/dashboard.module#DashboardModule', canActivate: [ AuthGuard ]}
];
@NgModule({
imports: [RouterModule.forRoot(routes, {preloadingStrategy: PreloadAllModules})],
exports: [RouterModule]
})
export class AppRoutingModule { }
问题是当用户尝试导航到仪表板模块时,canActivate总是返回false,因为我的switchMap操作符中的响应未定义。
编辑2:
我在我的代码中重构并简化了以下两种方法,现在一切正常。现在,我试图了解原因。以下是调整方法:
verifyUserRole(){
let headers = new Headers({ 'Authorization': 'Bearer ' + this.token});
let options = new RequestOptions({ headers: headers });
return this.http.get(environment.api+'/verifyadminuser', options).map((response)=>{return response});
}
verifyAdminUser():Observable<boolean>{
return this.getToken()
.map((user)=>{
user.getIdToken().then((token)=>this.token = token);
return user;
})
.map(()=>{return this.verifyUserRole})
.switchMap((response:any)=>{ return response ? Observable.of(true) : Observable.of(false)});
}
解决方案:
感谢@BeetleJuice下面的优秀答案,我能够创建衍生解决方案。 以下是我重构的两种方法:
verifyUserRole(token){
this.token = token;
let headers = new Headers({ 'Authorization': 'Bearer ' + token});
let options = new RequestOptions({ headers: headers });
return this.http.get(environment.api+'/verifyadminuser', options).map((response)=>{return response});
}
verifyAdminUser():Observable<boolean>{
return this.getToken()
.switchMap((user)=>{return Observable.fromPromise(user.getIdToken())})
.switchMap((token)=>{return this.verifyUserRole(token)})
.map((response:any)=>{ return response.status === 200 ? true : false});
}
答案 0 :(得分:5)
请注意这一行,取自verifyUserRole
console.log(token);// Log out "PromiseObservable...
所以token
是一个可观察的,但是你把它当作下一行的字符串对待,所以服务器可能会拒绝该请求
let headers = new Headers({ 'Authorization': 'Bearer ' + token})
您在.map
中滥用verifyAdminUser()
运算符。 map
只应与同步函数一起使用。例如:
// this works if user.id is a property available immediately
return this.getToken().map(user => user.id)
map
不应与异步函数一起使用。例如:
// this fails since the return value is an Observable that will resolve later
return this.getToken().map(user => Observable.fromPromise(...))
map
立即返回。因此,传递给链的内容不是您预期的令牌本身,而是将来会生成令牌的Observable。这就是console.log(token)
给你“PromiseObservable”的原因。你需要的是一个等待其中的observable生成的运算符,然后将发出的值传递给下一个运算符。使用switchMap
//this will work; switchMap waits for Obervable.fromPromise
// to emit before continuing
return this.getToken().switchMap(user => Observable.fromPromise(...))
所以基本上,在map
的第3行和第4行中将switchMap
替换为verifyAdminUser
。您也可以通过反向简化verifyAdminUser
的最后一行:更改
.switchMap((response:any)=>{ return response.status === 200 ? Observable.of(true) : Observable.of(false)})
与
// no need for switchMap because response.status is available immediately
.map(res => res.status===200? true: false)
此外,您错误地使用了canActivate
。这是为了防止组件的激活。要防止加载延迟加载的模块,请使用canLoad guard。所以我要用canActivate
替换canLoad
(如果我想保护整个模块),或者将canActivate
后卫移到具有component:
属性的特定路由中DashboardRoutingModule
(我猜这个名字)