【发布时间】:2017-06-23 22:08:35
【问题描述】:
我有一个实现 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>
如果有人有任何想法,还在寻找解决方案吗?
【问题讨论】:
-
我建议你做一个像 [authUrl] 这样的指令。在这些指令中实例化您的 Auth 服务并检查用户权限,基于此您可以显示/隐藏页面上的任何元素。
-
尚未处理的指令 - 我现在会查找它们,谢谢。
标签: angular typescript roles angular-routing