将参数传递给路线保护员

Bri*_*yes 74 typescript angular2-routing angular

我正在开发一个具有很多角色的应用程序,我需要使用防护来根据这些角色阻止导航到应用程序的某些部分.我意识到我可以为每个角色创建单独的防护类,但宁愿有一个我可以以某种方式传递参数的类.换句话说,我希望能够做类似的事情:

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

但是,既然你传递的只是你后卫的类型名称,就不会想到办法做到这一点.我是否应该咬紧牙关并为每个角色编写单独的防护等级,并在单一的参数化类型中打破我的优雅幻想?

小智 162

你必须这样做.

而不是使用forRole(),你应该使用这个:

{ 
   path: 'super-user-stuff', 
   component: SuperUserStuffComponent,
   canActivate: RoleGuard,
   data: {roles: ['SuperAdmin', ...]}
}
Run Code Online (Sandbox Code Playgroud)

并在您的RoleGuard中使用它

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

    let roles = route.data.roles as Array<string>;
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为这些数据的安全性无关紧要.您必须在服务器端使用身份验证和授权.我认为警卫的重点不是完全保护你的申请.如果有人"破解"它并导航到管理页面,他/她将无法从服务器获取安全数据,只是看到你认为管理组件是可以的.我认为这是更好的解决方案.接受的解决方案打破了依赖注入. (7认同)
  • 该解决方案效果很好。我的问题是它依赖于间接层。在不知道代码如何提前工作的情况下,查看此代码的任何人都不会意识到“ roles”对象和路由防护器是链接在一起的。令人惊讶的是,Angular不支持以更具声明性的方式执行此操作的方法。(要清楚,这是我在向Angular抱怨,而不是这个完全合理的解决方案。) (2认同)

tsi*_*iki 21

从 2022 年起,您可以使用 CanActivateFn ( https://angular.io/api/router/CanActivateFn )。该函数返回一个 CanActivateFn 实例:

// Returns a function which can act as a guard for a route
function requireAnyRole(...roles: Role[]): CanActivateFn {
  return (ars: ActivatedRouteSnapshot, rss: RouterStateSnapshot) => {
    // do some checks here and return true/false/observable
    // can even inject stuff with inject(ClassOrToken)
  }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以在定义路由时使用它

{
  path: 'some/path',
  component: WhateverComponent,
  canActivate: [requireAnyRole(Role1, Role2, Role3)]
}
Run Code Online (Sandbox Code Playgroud)


Ovi*_*lha 6

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

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

我们有一个用于处理是否经过授权的授权卫士的类:

@Injectable()
export class AuthGuardService implements CanActivate {

    checkUserLoggedIn() { ... }
Run Code Online (Sandbox Code Playgroud)

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

它还包含用于获取自定义权限保护的方法,该方法实际上取决于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;
  }
Run Code Online (Sandbox Code Playgroud)

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

....
{ path: 'something', 
  component: SomeComponent, 
  canActivate: [ AuthGuardService.forPermissions('permission1', 'permission2') ] },
Run Code Online (Sandbox Code Playgroud)

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

public static guards = [ ]; 
Run Code Online (Sandbox Code Playgroud)

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

providers: [
    // ...
    AuthGuardService,
    ...AuthGuardService.guards,
]
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助。


Tim*_*nko 6

方法与工厂功能的另一种选择组合data

export function canActivateForRoles(roles: Role[]) {
  return {data: {roles}, canActivate: [RoleGuard]}
}

export class RoleGuard implements CanActivate {
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
      : Observable<boolean> | Promise<boolean> | boolean  {
  
      const roles = route.data.roles as Role[];
    ...
  }
}

...

{ path: 'admin', component: AdminComponent, ...canActivateWithRoles([Role.Admin]) },

Run Code Online (Sandbox Code Playgroud)

  • 使用 [Spread Syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) 的限制 (`...`) 表示定义在您的路线将被 `...canActivateWithRoles([...])` 覆盖。例如,这将不起作用 `{/* ... */, data: { title: 'MyTitle' }, ...canActivateWithRoles([Role.Admin]) }`。 (5认同)

D L*_*Lai 6

你可以这样写你的角色守卫:

export class RoleGuard {
  static forRoles(...roles: string[]) {

    @Injectable({
      providedIn: 'root'
    })
    class RoleCheck implements CanActivate {
      constructor(private authService: AuthService) { }
        canActivate(): Observable<boolean> | Promise<boolean> | boolean {
          const userRole = this.authService.getRole();

          return roles.includes(userRole);
        }
      }

      return RoleCheck;
    }

}
Run Code Online (Sandbox Code Playgroud)

如果您愿意,也可以像这样将其用于多个角色:

{ 
  path: 'super-user-stuff', 
  component: SuperUserStuffComponent,
  canActivate: [RoleGuard.forRoles('superUser', 'admin', 'superadmin')]
}
Run Code Online (Sandbox Code Playgroud)