vlambda博客
学习文章列表

Angular 权限管理控制两种方案



在做后台管理系统的时候,权限管理应该是必备的功能点了。这一节我们介绍两种方案来确定用户权限。

首先,我们面板是这个样子,先让大家有一个基础印象:

Angular 权限管理控制两种方案


准备工作


  1. 首先我们通过 cli 工具新建了一个 heroes 模块,所有工作我们都将在这个模块中完成;

  2. 其次新建了 heroes-add 、 heroes-list 、 heroes-login 、 heroes-modify 四个页面模块,来分别实现不同的功能;

  3. 最后通过子路由的方式配置了项目的路由信息,以便让项目跑起来。

  4. 封装一些常用的方法为服务,以便多处使用:

  5. 添加请求拦截器,为已登录用户每次的请求头添加 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

Replay Share Like

0/0

进度条,百分之0
00:00
/
00:44
00:44
全屏

继续观看

Angular 权限管理控制两种方案

转载
,
Angular 权限管理控制两种方案
Angular教程

但是你会发现,我们还有个删除功能没做权限管理。 一般情况下,删除应该是不会跳转路由的,所以,我们需要另辟蹊径来处理。


通过指令控制权限

THIS IS TITLE


我们想要的结果其实就是: 根据角色,页面上只展示有权限的按钮或其他跟权限有关的入口。
ng g d demos/heroes/directives/auth
// auth.directive.tsimport {Directive, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';import {UserService} from '../services/user.service';
@Directive({ selector: '[appAuth]'})// 实现 OnChanges 接口export class AuthDirective implements OnChanges{ // 输入属性传值,获取有权限的角色 @Input('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; }}

使用指令:

Angular 权限管理控制两种方案

页面表现

Replay Share Like

0/0

进度条,百分之0
00:00
/
00:42
00:42
全屏

继续观看

Angular 权限管理控制两种方案

转载
,
Angular 权限管理控制两种方案
Angular教程

至此,我们就实现了通过角色来进行权限管理的全部功能。

通过动态配置权限实现权限管理

THIS IS TITLE


在实际工作中,我们可能还会遇到这样的情况:用户的角色是不固定的,所拥有的权限也是动态配置的。这样的情况,我们如果采用上面的方式来做权限,那势必会经常修改我们页面上所配置的角色。所以,针对这样的情况就要采取另一种方式。

我们打算通过页面名与后台传入的权限进行 viewnewdeleteedit 等相应的权限控制。

为了演示,我们将会新建四个 normal skill  grade  level 组件。normal 是没有被权限控制的,所有用户都可以访问。

假如每个登录用户信息是这样的:

{ "name": "卡特",  "rights": { "skill": ["edit", "new"], "grade": ["view"] }, ...}

上面表示:用户‘卡特’没有访问 level 页面的权限,可以在 skill 页面编辑、新建,在 grade 页面只能查看。

我们还是通过结构性指令来实现,如果没有权限,完全不显示对应入口的功能。

ng g d demos/heroes/directives/rights

对应需要控制的页面入口,我们通过传入页面名进行控制:

Angular 权限管理控制两种方案

页面中需要控制的操作入口,通过传入操作类型来进行控制:

Angular 权限管理控制两种方案

方案确定,只差实现指令:

// rights.directive.tsimport {Directive, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';import {UserService} from '../services/user.service';import { Router} from '@angular/router';
@Directive({ selector: '[appRights]'})export class RightsDirective implements OnChanges{ // 输入属性传值,获取配置 @Input('appRights') 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; }}

来看效果:

Replay Share Like

0/0

进度条,百分之0
00:00
/
00:37
00:37
全屏

继续观看

Angular 权限管理控制两种方案

转载
,
Angular 权限管理控制两种方案
Angular教程

想要的效果已经实现,通过页面名来匹配可能不是最好的解决方式,因为这样必须要求页面名是唯一的,如有更好的解决方案,欢迎私信~

其实这里还遇到一个问题:

权限管理必定会配合着路由懒加载,但是懒加载的组件是不需要在任何模块中  declarations  数组中引入的,如果没有引入组件,那么指令就不会在子模块中的组件中生效,会报错。  所以,最后的解决方式就是在提供指令的模块中同时引入懒加载路由的组件。不用担心,懒加载依然有意义。

Angular 权限管理控制两种方案


总结

THIS IS TITLE


  1. 在比较固定角色的情况下,采取“路由守卫 + 结构性指令”方案是不错的选择,相反的话第二种方式则更推荐;

  2. 权限管理必定会配合着路由懒加载。

权限管理的处理方式可能还有其他方案,如果你的更好,请告诉我~


点个赞跟再看吧,点个赞跟再看吧,点个赞跟再看吧~

感谢B站大佬咯咯各哒的支持,视频教程请访问B站 搜索‘“Angular10完全解读”

END


扫描二维码

获取更多精彩

Angular教程

Angular 权限管理控制两种方案



点个在看你最好看