Angular - 隐藏模板中的保护链接

时间:2017-06-23 10:35:52

标签: angular typescript roles angular-routing

我有一个实现CanActivate的AuthGuard,它根据一些自定义逻辑返回true或false:

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

import { AuthService } from '../services/auth.service';

import { UserTypes } from '../model/user-type';

@Injectable()
export class AuthGuard implements CanActivate {
    constructor(
        private router: Router,
        private authService: AuthService
    ) {
    }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        const allowedRoles = route.data['allowedRoles'] as Array<UserTypes>;
        if (!allowedRoles) {
            // All users allowed
            return true;
        }
        // Check user's role against the allowed roles defined
        const canActivate = (allowedRoles.indexOf(this.authService.userData.UserTypeId) !== -1);
        if (!canActivate) {
            this.router.navigate(['/portal']);
        }
        return canActivate;
    }
}

我需要两个canActivate参数(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)进行处理(并非所有代码都显示)。

我的路线定义了一些路线数据,告诉我哪些用户类型可以访问路线。当尝试访问路线时,这一切都非常好:

const routes: Routes = [
  {
    path: '',
    component: PortalLayoutComponent,
    children: [
      {
        path: '',
        canActivate: [AuthGuard],
        component: PortalDashboardComponent
      },
      {
        path: 'customers',
        canActivate: [AuthGuard],
        data: { allowedRoles: [UserTypes.Admin] },
        children: [
          { path: '', component: PortalCustomerListComponent }
        ]
      }
    ]
  }
];

如果 - 对于任何给定的链接 - canActivate返回false,我想隐藏模板中的链接。我不知道如何实现这一目标。这是模板中的典型链接:

<a [routerLink]="['customers']">List Customers</a>

如何在不重复我已加入AuthGuard的用户类型逻辑的情况下如何禁用此功能?我试图将AuthGuard注入到我的组件中,但我无法弄清楚如何提供canActivate所需的两个参数?这是一个示例组件,其中包含一些目前正在努力工作的测试代码:

import { Component } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Constants } from '../../app.constants';

import { AuthGuard } from '../../guards/auth.guard';

@Component({
  templateUrl: './portal-dashboard.component.html',
  styleUrls: ['./portal-dashboard.component.scss']
})
export class PortalDashboardComponent {
  constructor(
    private authGuard: AuthGuard,
    private activatedRoute: ActivatedRoute,
    private router: Router
  ) { }

  public get canActivateLink(targetUrl: string): boolean {
    **// TODO: Use targetLink here to get user role rules from the route data?** 
    return this.authGuard.canActivate(this.activatedRoute.snapshot, this.router.routerState.snapshot);
  }
}

如果我能够完成上述工作,我只需在模板中执行以下操作(不是100%DRY,而是将重复角色逻辑添加到控制器中以用于每个链接):

<a [routerLink]="['customers']" *ngIf="canActivateLink('customers')">List Customers</a>

更新

感谢Yordan,我设法让这项工作成为指令。但是,我仍然有完全相同的问题,因为我无法找到一种简单的方法来获取特定URL的路由信息​​(即路由数据)。这是我到目前为止所得到的:

import { Input, OnInit, Directive, ViewContainerRef, TemplateRef } from '@angular/core';
import { LocationStrategy } from '@angular/common';
import { Router, ActivatedRoute, UrlTree } from '@angular/router';

import { AuthService, AuthState } from '../../services/auth.service';

import { UserTypes } from '../../model/user-type';

@Directive({
    selector: '[hiddenIfUnauthorised]'
})
export class HiddenIfUnauthorisedDirective implements OnInit {

    private commands: any[] = [];

    constructor(
        private templateRef: TemplateRef<any>,
        private viewContainer: ViewContainerRef,
        private locationStrategy: LocationStrategy,
        private router: Router,
        private route: ActivatedRoute,
        private auth: AuthService
    ) { }

    @Input()
    set hiddenIfUnauthorised(commands: any[] | string) {
        if (commands != null) {
            this.commands = Array.isArray(commands) ? commands : [commands];
        } else {
            this.commands = [];
        }
        console.log(this.commands);
    }

    get urlTree(): UrlTree {
        // TODO: Mimic the rest of the relevant options as per the RouterLink source
        return this.router.createUrlTree(this.commands, {
            relativeTo: this.route
        });
    }

    public ngOnInit() {
        const urlTree = this.urlTree;
        const sUrl = this.router.serializeUrl(urlTree);
        const url = this.locationStrategy.prepareExternalUrl(sUrl);

        // TODO: I need to generate an ActivatedRoute object for the "url" created above
        // so I can get 'allowedRoles' from route data - or get it some other way.
        // Are there any helper methods anywhere?
        const targetRoute = this.route;

        const userTypes = targetRoute.data['allowedRoles'] as Array<UserTypes>;

        const authState = this.auth.getAuthState(userTypes);

        if (authState !== AuthState.Authorised) {
            // Not authorised, remove the DOM container
            this.viewContainer.clear();
        } else {
            // Show the DOM container
            this.viewContainer.createEmbeddedView(this.templateRef);
        }
    }
}

这是在我的模块中注册的,然后在模板中使用如下:

<div *hiddenIfUnauthorised="['customers']">
Secure Content!
</div>

如果有人有任何想法,还在寻找解决方案吗?

1 个答案:

答案 0 :(得分:0)

这是我的意思的快速代码

import { Directive, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({ selector: '[permission]' })
export class IfDirective {
  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef,
    private auth: AuthService
    ) { }

  @Input() permission: string;

  public ngOnInit() {
    if(this.auth.hasPermission) {
      // If condition is true add template to DOM
      this.viewContainer.createEmbeddedView(this.templateRef);
     } else {
     // Else remove template from DOM
      this.viewContainer.clear();
    }
  }

}

被修改