rev*_*ver 29 routing typescript angular2-routing angular
我有一个有两名canActivate守卫的路线(AuthGuard和RoleGuard).first(AuthGuard)检查用户是否已登录,如果没有,则重定向到登录页面.第二个检查用户是否具有允许查看页面的角色定义,如果没有,则重定向到未授权页面.
canActivate: [ AuthGuard, RoleGuard ]
...
export class AuthGuard implements CanActivate {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
...
this.router.navigate(['/login']);
resolve(false);
}
export class RoleGuard implements CanActivate {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
...
this.router.navigate(['/unauthorized']);
resolve(false);
}
Run Code Online (Sandbox Code Playgroud)
问题是,当我访问路线并且我没有登录时,我点击了AuthGuard,它失败并告诉路由器导航到/login.然而,即使AuthGuard失败RoleGuard了,然后运行然后导航到/unauthorized.
在我看来,如果第一个失败,那么运行下一个后卫是毫无意义的.有没有办法强制执行此行为?
Pie*_*Duc 28
这是因为你正在返回一个Promise<boolean>而不仅仅是一个boolean.如果你只是返回一个布尔值,它将不会检查RoleGuard.我猜这可能是angular2异步请求中的错误或预期的结果.
但是,您可以通过仅使用需要RoleGuard某个URL的URL 来解决此问题Role,因为我猜您需要登录才能拥有角色.在这种情况下,您可以将您更改RoleGuard为:
@Injectable()
export class RoleGuard implements CanActivate {
constructor(private _authGuard: AuthGuard) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return this._authGuard.canActivate(route, state).then((auth: boolean) => {
if(!auth) {
return false;
}
//... your role guard check code goes here
});
}
}
Run Code Online (Sandbox Code Playgroud)
Dam*_*n C 18
从 Angular 8 开始,我能够做到这一点。该解决方案的灵感来自@planet_hunter 的答案,但代码较少,并且使用observables来完成这个项目的要求。
使用您选择的名字创建一个守卫,该守卫将按顺序运行所有守卫。
@Injectable({
providedIn: 'root'
})
export class SyncGuardHelper implements CanActivate {
public constructor(public injector: Injector) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
return from(route.data.syncGuards).pipe(concatMap((value) => {
const guard = this.injector.get(value);
const result = guard.canActivate(route, state);
if (result instanceof Observable) {
return result;
} else if (result instanceof Promise) {
return from(result);
} else {
return of(result);
}
}), first((x) => x === false || x instanceof UrlTree, true));
}
}
Run Code Online (Sandbox Code Playgroud)
在您的路由文件中,使用 data 属性添加要按顺序(同步)运行的守卫:
const routes: Routes = [
{
path: '',
component: MyComponent,
canActivate: [SyncGuardHelper],
data: {
syncGuards: [
Guard1,
Guard2,
Guard3
]
}
},
// other routes
]
Run Code Online (Sandbox Code Playgroud)
我今天必须提出这个解决方案,所以如果您有任何反馈,请发表评论,以便我改进这个答案。
Wil*_*ilt 11
此问题已在 Angular 7.1 及更高版本中解决。
守卫现在有了优先权。
可以在这篇很棒的博客文章中找到有关它如何工作的详细说明。
我从博客文章中引用以下示例:
Run Code Online (Sandbox Code Playgroud)canActivate: [CanActivateRouteGuard, CanActivateRouteGuard2],
这将按如下方式工作:
给定
canActivate数组中的所有守卫都是并行执行的,但路由器将等到任何具有更高优先级的守卫完成后再继续。所以在上面的例子中:
- 即使立即
CanActivateRouteGuard2返回UrlTree:
路由器仍将CanActivateRouteGuard在启动新导航之前等待解析。- 如果
CanActivateRouteGuard返回一个UrlTree:
那将获胜。- 如果它返回
false:
整个导航失败(并且没有重定向发生)。- 如果它只是返回
true:
那么UrlTree返回的 byCanActivateRouteGuard2将被导航到。
当前有多个异步守卫(返回 Promise 或 Observable)将同时运行。我为此开了一个问题:https : //github.com/angular/angular/issues/21702
上述解决方案的另一种解决方法是使用嵌套路由:
{
path: '',
canActivate: [
AuthGuard,
],
children: [
{
path: '',
canActivate: [
RoleGuard,
],
component: YourComponent
// or redirectTo
// or children
// or loadChildren
}
]
}
Run Code Online (Sandbox Code Playgroud)
正如@PierreDuc data中RouteClass 中的属性所提到的那样,可以使用Master Guard来解决此问题。
首先,angular不支持串联调用后卫的功能。因此,如果第一个防护是异步的并且正在尝试进行ajax调用,则即使在防护1中完成ajax请求之前,所有其余的防护也会被触发。
我遇到了类似的问题,这就是我解决的方法-
这个想法是创建一个主警卫,并让该主警卫处理其他警卫的执行。
该路由配置在这种情况下,将包含主卫作为唯一的后卫。
要使主要警卫知道要针对特定路线触发的警卫,请在中添加一个data属性Route。
该data属性是一个键值对,允许我们将数据附加到路由。
然后可以使用防护中方法的ActivatedRouteSnapshot参数在防护中访问数据canActivate。
该解决方案看起来很复杂,但是一旦将其集成到应用程序中,就可以确保防护装置正常工作。
以下示例说明了这种方法-
1.常量对象,用于映射所有应用程序防护-
export const GUARDS = {
GUARD1: "GUARD1",
GUARD2: "GUARD2",
GUARD3: "GUARD3",
GUARD4: "GUARD4",
}
Run Code Online (Sandbox Code Playgroud)
2. Application Guard-
import { Injectable } from "@angular/core";
import { Guard4DependencyService } from "./guard4dependency";
@Injectable()
export class Guard4 implements CanActivate {
//A guard with dependency
constructor(private _Guard4DependencyService: Guard4DependencyService) {}
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return new Promise((resolve: Function, reject: Function) => {
//logic of guard 4 here
if (this._Guard4DependencyService.valid()) {
resolve(true);
} else {
reject(false);
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
3.路由配置-
import { Route } from "@angular/router";
import { View1Component } from "./view1";
import { View2Component } from "./view2";
import { MasterGuard, GUARDS } from "./master-guard";
export const routes: Route[] = [
{
path: "view1",
component: View1Component,
//attach master guard here
canActivate: [MasterGuard],
//this is the data object which will be used by
//masteer guard to execute guard1 and guard 2
data: {
guards: [
GUARDS.GUARD1,
GUARDS.GUARD2
]
}
},
{
path: "view2",
component: View2Component,
//attach master guard here
canActivate: [MasterGuard],
//this is the data object which will be used by
//masteer guard to execute guard1, guard 2, guard 3 & guard 4
data: {
guards: [
GUARDS.GUARD1,
GUARDS.GUARD2,
GUARDS.GUARD3,
GUARDS.GUARD4
]
}
}
];
Run Code Online (Sandbox Code Playgroud)
4. Master Guard-
import { Injectable } from "@angular/core";
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from "@angular/router";
//import all the guards in the application
import { Guard1 } from "./guard1";
import { Guard2 } from "./guard2";
import { Guard3 } from "./guard3";
import { Guard4 } from "./guard4";
import { Guard4DependencyService } from "./guard4dependency";
@Injectable()
export class MasterGuard implements CanActivate {
//you may need to include dependencies of individual guards if specified in guard constructor
constructor(private _Guard4DependencyService: Guard4DependencyService) {}
private route: ActivatedRouteSnapshot;
private state: RouterStateSnapshot;
//This method gets triggered when the route is hit
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
this.route = route;
this.state = state;
if (!route.data) {
Promise.resolve(true);
return;
}
//this.route.data.guards is an array of strings set in routing configuration
if (!this.route.data.guards || !this.route.data.guards.length) {
Promise.resolve(true);
return;
}
return this.executeGuards();
}
//Execute the guards sent in the route data
private executeGuards(guardIndex: number = 0): Promise<boolean> {
return this.activateGuard(this.route.data.guards[guardIndex])
.then(() => {
if (guardIndex < this.route.data.guards.length - 1) {
return this.executeGuards(guardIndex + 1);
} else {
return Promise.resolve(true);
}
})
.catch(() => {
return Promise.reject(false);
});
}
//Create an instance of the guard and fire canActivate method returning a promise
private activateGuard(guardKey: string): Promise<boolean> {
let guard: Guard1 | Guard2 | Guard3 | Guard4;
switch (guardKey) {
case GUARDS.GUARD1:
guard = new Guard1();
break;
case GUARDS.GUARD2:
guard = new Guard2();
break;
case GUARDS.GUARD3:
guard = new Guard3();
break;
case GUARDS.GUARD4:
guard = new Guard4(this._Guard4DependencyService);
break;
default:
break;
}
return guard.canActivate(this.route, this.state);
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法的挑战之一是重构现有的路由模型。但是,由于更改是不间断的,因此可以部分完成。
我希望这有帮助。
| 归档时间: |
|
| 查看次数: |
23023 次 |
| 最近记录: |