带有用户角色参数的 Angular2 路由 canActivate 和 AuthGuard (JWT)

Posted

技术标签:

【中文标题】带有用户角色参数的 Angular2 路由 canActivate 和 AuthGuard (JWT)【英文标题】:Angular2 routing canActivate and AuthGuard (JWT) with user role parameter 【发布时间】:2016-11-19 01:22:45 【问题描述】:

在这个带有 JWT 身份验证的exaple project 中,我们了解如何只允许经过身份验证的用户访问某些路由:

import  RouterConfig  from '@angular/router';
import  Home  from './home';
import  Login  from './login';
import  Signup  from './signup';
import  AuthGuard  from './common/auth.guard';

export const routes: RouterConfig = [
   path: '',       component:  Login ,
   path: 'login',  component: Login ,
   path: 'signup', component: Signup ,
   path: 'home',   component: Home, canActivate: [AuthGuard] ,
   path: '**',     component: Login ,
];

我想更进一步,并指出哪些用户角色可以“访问”路由 - 但我不知道如何将参数传递给 canActivate AuthGuard (src)。所以我想实现这样的目标(例如我有两个角色:管理员和员工):

   path: 'home',   component: Home, canActivate: [AuthGuard] ,
   path: 'users',   component: AdminUsers, canActivate: [AuthGuard('Admin')] ,
   path: 'users',   component: Employees, canActivate: [AuthGuard('Employee')] ,

我的 AuthGuard 可能看起来像这样(其中 userRole(= Admin or Employee or null) 将参数传递给 AuthGuard):

@Injectable()
export class AuthGuard implements CanActivate 
  constructor(private router: Router) 

  canActivate(userRole) 
    if (!userRole || JWT.user().role == userRole) 
      return true;
    

    this.router.navigate(['/login']);
    return false;
  

其中 JWT.user.role 是读取存储在 JWT 令牌中的用户角色的助手。有没有办法做类似上述想法的事情?

【问题讨论】:

我们不能将 AuthGuard 扩展为 AdminAuthGuard 和 UserAuthGuard 并在死记硬背的声明中将它们传递给 canActivate 钩子 我知道,这样 - 这是解决方法。但我想知道,确实存在类似于上述问题想法的其他方式。 我不这么认为,因为在路由声明期间,您只指定了警卫类。你可以通过多个类。 我是 Angular5 的新手,我正在努力解决同样的问题。如果您的问题得到解决,那么您可以分享您的代码吗? 【参考方案1】:

CanActivate 的签名不允许你像你想要的那样传递userRole。 https://github.com/angular/angular/blob/2.0.0-rc.4/modules/%40angular/router/src/interfaces.ts#L54

最好为每个用户角色案例做单独的类。这也是官方文档中的指导:https://angular.io/docs/ts/latest/api/router/index/CanActivate-interface.html

【讨论】:

这个答案是正确的,在我提出问题的时候 - 这就是为什么绿色“检查”在这里的原因。但是对于新的角度版本,有更好的解决方案(阅读下面的答案)【参考方案2】:

注意:适用于angular-rc.4

@KamilKiełczewski,@NikolayRusev,

    在路由数组中添加了附加数据:

...

    path: "customers",
    component: CustomersCmp,
    data:  roles: ["admin"] 
,
...

在 CanActivate 中,您可以从第一个参数中获取 path,在路由配置中搜索相同的路径并从数据中获取您描述的角色:

public canActivate(route: ActivatedRouteSnapshot): boolean 
    let path = route._urlSegment.pathsWithParams[0].path;
    let roles;

    if (route._routeConfig.path == path) 
        roles = route._routeConfig.data.roles
     else 
        roles = route._routeConfig.children.find(_route => _route.path == path).data.roles;
    

    if (...) 
        return true;
    

    return false;

当然最好避免私有属性,你可以,但我不记得我到底是怎么做到的。

但是对于我的海豚,我以不同的方式重新制作了它。这种方法的巨大缺点,我的意思是基于角色的守卫,如果你在组件中自动而不是手动渲染它们,每个具有不同角色的用户都可以看到所有路由。

    你可以扩展router-outlet http://www.captaincodeman.com/2016/03/31/angular2-route-security/ https://medium.com/@blacksonic86/authentication-in-angular-2-958052c64492#.cxgoan9go

【讨论】:

很高兴听到来自 angular2 团队成员之一的评论。目前,似乎大多数人都在按照 Andrei Shostik 的方式进行操作,这似乎很老套。您需要在加载时阅读角色,然后通过您的路线并根据路线检查角色。如果故意限制无法访问 CanActivate 中的数据,我们不应该使用它。并且知道正确的方法会很好。如果它不是故意的,我们需要知道“正确”的解决方法。似乎也是相同的方法:***.com/a/39097563/145334 对于我之前的评论,似乎 github 上的这个问题是相关的:github.com/angular/angular/issues/9001 也针对这种情况提交了问题:github.com/angular/angular/issues/11708【参考方案3】:

可以像这样设置路由的data参数

const appRoutes: Routes = [
 
  path: 'account/super-secure', 
  component: SuperSecureComponent, 
  canActivate: [RoleGuard], 
  data:  roles: ['super-admin', 'admin']  
];

然后在canActivateRoleGuard 中有这个:

canActivate(route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): boolean 

    let roles = route.data["roles"] as Array<string>;
    return (roles == null || roles.indexOf("the-logged-user-role") != -1);

我认为这可能是另一种方法,而不是为每个角色创建后卫。我实际上会采用这种方式,因为它需要更少的代码并且可以很好地处理问题。

【讨论】:

当我尝试使用您的解决方案时,我看到以下错误:Property 'data' does not exist on type 'ActivatedRouteSnapshot'. 我认为您的解决方案仅适用于旧版本的 angular2 @Kamil Kietczewski,该解决方案至少适用于 angular 2 (~2.0.1) 和新路由器 ~3.0.1。可能是您使用的是旧路由器。 我为 Angular 2.0.0-rc.3 测试了它 - 我在我的项目中使用这个版本 - 但我会尝试升级我的项目并测试你的解决方案......所以请耐心等待 如果你的用户可以有多个角色,你可以编辑这个来找出 route.roles 数组和 user.roles 数组的交集:user.roles.filter(r =&gt; route.roles.indexOf(r) !== -1).length &gt; 0 这似乎是访问路由的方法,但是根据角色显示或隐藏组件怎么样?

以上是关于带有用户角色参数的 Angular2 路由 canActivate 和 AuthGuard (JWT)的主要内容,如果未能解决你的问题,请参考以下文章

Angular2:无路由的延迟加载和动态加载

带有标签的Angular2路由到页面锚点

路由器插座的Angular 2延迟渲染

重新路由到带有/用户数据 Angular 2 和 Firebase 的个人资料页面

如何从Angular2中的父组件获取子路由的:id参数

带有 Auth0 路由错误 404 的 Angular 2