将参数传递到路径防护中


102

我正在开发一个具有许多角色的应用程序,我需要使用这些功能来根据这些角色来阻止对应用程序各部分的导航。我意识到我可以为每个角色创建单独的后卫类,但宁愿拥有一个可以以某种方式将参数传递给它的类。换句话说,我希望能够执行以下操作:

{ 
  path: 'super-user-stuff', 
  component: SuperUserStuffComponent,
  canActivate: [RoleGuard.forRole('superUser')]
}

但是,由于您所传递的只是警卫的类型名称,因此无法考虑实现此目标的方法。我是否应该只是硬着头皮,为每个角色编写单独的后卫班级,而打乱我拥有单一参数化类型的优雅幻想呢?

Answers:


218

除了使用forRole(),您可以执行以下操作:

{ 
   path: 'super-user-stuff', 
   component: SuperUserStuffComponent,
   canActivate: RoleGuard,
   data: {roles: ['SuperAdmin', ...]}
}

并在您的RoleGuard中使用它

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
    : Observable<boolean> | Promise<boolean> | boolean  {

    let roles = route.data.roles as Array<string>;
    ...
}

很好的选择,谢谢。我喜欢Aluan的工厂方法略胜一筹,但感谢您在这种可能性上的投入!
Brian Noyes

7
我认为此数据的安全性无关紧要。您必须在服务器端使用身份验证和授权。我认为保护的重点不是完全保护您的应用程序。如果有人“黑客”它并导航到管理页面,他/她将不会仅从服务器上看到管理组件,就不会从服务器获取安全数据,我认为这是可以的。我认为这是公认的更好的解决方案。接受的解决方案破坏了依赖注入。
bucicimaci

1
这是一个很好的解决方案,在我的通用AuthGuard中效果很好。
SAV

3
该解决方案效果很好。我的问题是它依赖于间接层。在roles不知道代码如何提前工作的情况下,查看此代码的任何人都无法意识到对象和路由保护程序已链接在一起。令人惊讶的是,Angular不支持以更具声明性的方式执行此操作的方法。(要清楚,这是我在向Angular
抱怨,

1
@KhalilRavanna谢谢您,是的,但是我很早以前就使用了此解决方案,所以我转到了另一个解决方案。我的新解决方案是一个RoleGaurd和一个名称为“ access.ts”且文件中具有Map <URL,AccessRoles>常量的文件,然后在RoleGaurd中使用它。如果您想控制应用程序中的访问,我认为这种新方法要好得多,尤其是当您在一个项目中拥有多个应用程序时。
哈桑·贝赫什蒂

11

这是我的看法,也是针对缺少的提供程序问题的可能解决方案。

以我为例,我们有一个将权限或权限列表作为参数的防护,但是具有作用的是同一件事。

我们有一个用于处理经过授权或未经授权的auth Guard的类:

@Injectable()
export class AuthGuardService implements CanActivate {

    checkUserLoggedIn() { ... }

这涉及检查用户活动会话等。

它还包含用于获取自定义权限保护的方法,该方法实际上取决于AuthGuardService自身

static forPermissions(permissions: string | string[]) {
    @Injectable()
    class AuthGuardServiceWithPermissions {
      constructor(private authGuardService: AuthGuardService) { } // uses the parent class instance actually, but could in theory take any other deps

      canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        // checks typical activation (auth) + custom permissions
        return this.authGuardService.canActivate(route, state) && this.checkPermissions();
      }

      checkPermissions() {
        const user = ... // get the current user
        // checks the given permissions with the current user 
        return user.hasPermissions(permissions);
      }
    }

    AuthGuardService.guards.push(AuthGuardServiceWithPermissions);
    return AuthGuardServiceWithPermissions;
  }

这使我们能够使用该方法基于路由模块中的权限参数来注册一些自定义防护:

....
{ path: 'something', 
  component: SomeComponent, 
  canActivate: [ AuthGuardService.forPermissions('permission1', 'permission2') ] },

有趣的部分forPermissionAuthGuardService.guards.push-这基本上可以确保forPermissions在调用任何时间来获取自定义防护类时,也会将其存储在此数组中。这在主类上也是静态的:

public static guards = [ ]; 

然后,我们可以使用该数组来注册所有的守护程序-可以,只要确保在应用模块注册这些提供程序时,已经定义了路由并创建了所有的守护程序类(例如,检查导入顺序和将这些提供者的数量保持在列表中尽可能低的位置-使用路由模块有帮助):

providers: [
    // ...
    AuthGuardService,
    ...AuthGuardService.guards,
]

希望这可以帮助。


1
此解决方案给了我一个静态错误:ERROR中的ERROR静态解析符号值。
Arninja

该解决方案对我来说很适合开发,但是当我为投产而构建生产应用程序时ERROR in Error during template compile of 'RoutingModule' Function calls are not supported in decorators but 'PermGuardService' was called.
kpacn

这对具有自己的路由模块的延迟加载模块有效吗?
暗恋

2

@AluanHaddad的解决方案给出了“没有提供程序”错误。这是一个解决办法(感觉很脏,但是我缺乏制作更好的技巧)。

从概念上讲,我将由创建的每个动态生成的类注册为提供程序roleGuard

因此,对于每个检查的角色:

canActivate: [roleGuard('foo')]

你应该有:

providers: [roleGuard('foo')]

但是,roleGuard即使roles参数相同,@AluanHaddad的解决方案仍会为每次调用生成新类。使用lodash.memoize它看起来像这样:

export var roleGuard = _.memoize(function forRole(...roles: string[]): Type<CanActivate> {
    return class AuthGuard implements CanActivate {
        canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
            Observable<boolean>
            | Promise<boolean>
            | boolean {
            console.log(`checking access for ${roles.join(', ')}.`);
            return true;
        }
    }
});

注意,每个角色组合都会生成一个新类,因此您需要将每个角色组合注册为提供程序。即如果您有:

canActivate: [roleGuard('foo')]并且canActivate: [roleGuard('foo', 'bar')]您必须同时注册两个:providers[roleGuard('foo'), roleGuard('foo', 'bar')]

更好的解决方案是在内部的全局提供程序集合中自动注册提供程序roleGuard,但是正如我所说,我缺乏实现该功能的技能。


我真的很喜欢这种功能性方法,但是带有DI(class)的mixin闭包看起来像是开销。
Bill
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.