Angular 权限管理控制两种方案
首先,我们面板是这个样子,先让大家有一个基础印象:
准备工作
首先我们通过
cli
工具新建了一个heroes
模块,所有工作我们都将在这个模块中完成;其次新建了
heroes-add
、heroes-list
、heroes-login
、heroes-modify
四个页面模块,来分别实现不同的功能;最后通过子路由的方式配置了项目的路由信息,以便让项目跑起来。
封装一些常用的方法为服务,以便多处使用:
添加请求拦截器,为已登录用户每次的请求头添加
token
。(拦截器请参照7.2节介绍)
使用路由守卫控制权限
THIS IS TITLE
目前我们项目的状态是:无论用户是否登录,或者登录用户的权限如何,都能直接进行新增、修改、删除等操作。显然,这不是我们想要的。
所以,我们可以通过路由守卫来控制权限。
我们先给角色分配一下权限:
-
superadmin: 拥有所有权限; -
admin: 只有修改权限,没有删除、新增权限; -
user: 只有查看权限,没有操作权限。
给路由配置添加角色(roles
数组):
const routes: Routes = [
{
path: 'heroes',
component: HeroesComponent,
children: [
{ path: 'list', component: HeroesListComponent},
{
path: 'login',
loadChildren: () => import('./heroes-login/heroes-login.module').then(m => m.HeroesLoginModule),
canActivate: [LoginAuthGuard]
},
{
path: 'add',
loadChildren: () => import('./heroes-add/heroes-add.module').then(m => m.HeroesAddModule),
canActivate: [AuthGuard],
data: {roles: ['superadmin']}
},
{
path: 'modify/:id',
loadChildren: () => import('./heroes-modify/heroes-modify.module').then(m => m.HeroesModifyModule),
canActivate: [AuthGuard],
data: {roles: ['superadmin', 'admin']}
},
{ path: '', redirectTo: 'list', pathMatch: 'full' }
]
}
];
auth
守卫:
ng g g demos/heroes/guards/auth
// auth.guard.ts
// ...
export class AuthGuard implements CanActivate {
constructor(
private userServe: UserService,
private router: Router,
private accountServe: AccoutService,
private windowServe: WindowService
) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
// 获取即将进入路由的角色配置
const roles: string[] = route.data.roles;
return this.userServe.user$.pipe(
switchMap(user => {
// 判断用户是否登录
if (user) {
// 匹配用户角色与路由权限配置
if (roles.includes(user.role)) {
return of(true);
} else {
this.windowServe.alert('没有权限');
return of(false);
}
}
// 未登录,去登录,拦截进入下个路由
this.accountServe.redirectTo = state.url;
this.windowServe.alert('请先登录');
this.router.navigateByUrl('/heroes/login').then();
return of(false);
})
);
}
}
这样,我们就能大概实现拦截功能:
tips: 艾科--user 莫甘娜--superadmin 卡特--admin
0/0
继续观看
Angular 权限管理控制两种方案
但是你会发现,我们还有个删除功能没做权限管理。 一般情况下,删除应该是不会跳转路由的,所以,我们需要另辟蹊径来处理。
通过指令控制权限
THIS IS TITLE
ng g d demos/heroes/directives/auth
// auth.directive.ts
import {Directive, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
import {UserService} from '../services/user.service';
({
selector: '[appAuth]'
})
// 实现 OnChanges 接口
export class AuthDirective implements OnChanges{
// 输入属性传值,获取有权限的角色
'appAuth') roles: string[] = []; (
hasView = false;
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private userServe: UserService
) {}
// 在 ngOnChanges 阶段才能拿到输入属性的传值
ngOnChanges(changes: SimpleChanges): void {
if (this.roles.length) {
this.userServe.user$.subscribe(res => {
// 没匹配到角色
if (this.roles.includes(res?.role)){
this.createView();
} else {
this.viewContainer.clear();
this.hasView = false;
}
});
} else {
this.createView();
}
}
// 创建视图
private createView(): void {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
}
}
使用指令:
0/0
继续观看
Angular 权限管理控制两种方案
通过动态配置权限实现权限管理
THIS IS TITLE
在实际工作中,我们可能还会遇到这样的情况:用户的角色是不固定的,所拥有的权限也是动态配置的。这样的情况,我们如果采用上面的方式来做权限,那势必会经常修改我们页面上所配置的角色。所以,针对这样的情况就要采取另一种方式。
我们打算通过页面名与后台传入的权限进行 view
、new
、delete
、edit
等相应的权限控制。
为了演示,我们将会新建四个 normal
、 skill
、 grade
、 level
组件。normal
是没有被权限控制的,所有用户都可以访问。
假如每个登录用户信息是这样的:
{
"name": "卡特",
"rights": {
"skill": ["edit", "new"],
"grade": ["view"]
},
...
}
上面表示:用户‘卡特’没有访问 level
页面的权限,可以在 skill
页面编辑、新建,在 grade
页面只能查看。
我们还是通过结构性指令来实现,如果没有权限,完全不显示对应入口的功能。
ng g d demos/heroes/directives/rights
对应需要控制的页面入口,我们通过传入页面名进行控制:
方案确定,只差实现指令:
// rights.directive.ts
import {Directive, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
import {UserService} from '../services/user.service';
import { Router} from '@angular/router';
selector:
})
export class RightsDirective implements OnChanges{
// 输入属性传值,获取配置
''; rights =
hasView = false;
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private userServe: UserService,
private router: Router
) {}
ngOnChanges(changes: SimpleChanges): void {
const pageName = this.getPageName(this.router.url);
if (this.rights) {
this.userServe.user$.subscribe(res => {
if (res?.rights) {
if (
res.rights[this.rights] || /* 匹配页面入口 */
(res.rights[pageName] && res.rights[pageName].includes(this.rights)) /* 匹配页面操作入口 */
) {
this.createView();
}
} else {
this.clearView();
}
});
} else {
this.clearView();
}
}
// 创建视图
private createView(): void {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
}
// 清除视图
private clearView(): void {
this.viewContainer.clear();
this.hasView = false;
}
// 通过URL获取页面名
private getPageName(url: string): string {
const str = url.split('/').pop();
if (str.includes('?')) {
return str.split('?')[0];
} else if (str.includes('#')){
return str.split('#')[0];
}
return str;
}
}
来看效果:
0/0
继续观看
Angular 权限管理控制两种方案
想要的效果已经实现,通过页面名来匹配可能不是最好的解决方式,因为这样必须要求页面名是唯一的,如有更好的解决方案,欢迎私信~
其实这里还遇到一个问题:
declarations
数组中引入的,如果没有引入组件,那么指令就不会在子模块中的组件中生效,会报错。
所以,最后的解决方式就是在提供指令的模块中同时引入懒加载路由的组件。不用担心,懒加载依然有意义。
总结
THIS IS TITLE
在比较固定角色的情况下,采取“路由守卫 + 结构性指令”方案是不错的选择,相反的话第二种方式则更推荐;
权限管理必定会配合着路由懒加载。
点个赞跟再看吧,点个赞跟再看吧,点个赞跟再看吧~
END
扫描二维码
获取更多精彩
Angular教程
点个在看你最好看