Angular 权限管理的两种解决方案

Angular 权限管理的两种解决方案

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

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

在这里插入图片描述

准备工作

  1. 首先我们通过 cli工具新建了一个 heroes模块,所有工作我们都将在这个模块中完成;
  2. 其次新建了 heroes-addheroes-listheroes-loginheroes-modify四个页面模块,来分别实现不同的功能;
  3. 最后通过子路由的方式配置了项目的路由信息,以便让项目跑起来。
  4. 封装一些常用的方法为服务,以便多处使用:
  5. 添加请求拦截器,为已登录用户每次的请求头添加 token。(拦截器请参照7.2节介绍)

使用路由守卫控制权限

目前我们项目的状态是:无论用户是否登录,或者登录用户的权限如何,都能直接进行新增、修改、删除等操作。显然,这不是我们想要的。

所以,我们可以通过路由守卫来控制权限。

我们先给角色分配一下权限:

  • 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

在这里插入图片描述

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

通过指令控制权限

我们想要的结果其实就是:根据角色,页面上只展示有权限的按钮或其他跟权限有关的入口。

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';

@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;
  }
}

使用指令:

在这里插入图片描述

页面表现:

在这里插入图片描述

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

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

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

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

为了演示,我们将会新建四个 normalskillgradelevel组件。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';

@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;
  }
}

来看效果:

在这里插入图片描述

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

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

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

在这里插入图片描述

总结

1. 在比较固定角色的情况下,采取“路由守卫 + 结构性指令”方案是不错的选择,相反的话第二种方式则更推荐;
2. 权限管理必定会配合着路由懒加载。

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


感谢你的阅读,如果你觉得有用,欢迎评论、点赞、转发~
当然也希望你能关注我的公众号:前端大乱炖
在这里插入图片描述

  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Angular是一种流行的前端开发框架,它使用了TypeScript语言来构建现代化的Web应用程序。Angular后台管理系统是基于Angular框架开发的一种管理系统,用于管理和监控网站或应用程序的后台操作。它提供了丰富的功能和工具,使开发者能够轻松创建用户友好且高效的管理界面。 Angular后台管理系统主要有以下特点: 1.模块化架构:Angular后台管理系统使用模块化架构,将不同的功能模块分开开发。这种架构使得代码更易于维护和重用,并且可以方便地添加新的功能。 2.响应式设计:Angular后台管理系统支持响应式设计,可以在不同的终端设备上自适应显示。无论是在电脑、平板还是手机上,用户都能够获得一致的用户体验。 3.数据驱动视图:Angular使用数据绑定技术,将数据和视图进行绑定。这意味着当数据发生变化时,视图会自动更新,使得用户能够实时地查看和操作数据。 4.强大的表单验证:Angular提供了强大的表单验证机制,可以对用户输入的数据进行验证和处理。这样可以避免用户输入错误或恶意输入,提高数据的安全性和准确性。 5.丰富的组件库:Angular有一个丰富的组件库,开发者可以直接使用这些组件来构建页面。这些组件包括按钮、导航栏、表格等,可以大大加快开发速度。 总之,Angular后台管理系统是一种强大且高效的工具,可以帮助开发者快速构建现代化的用户管理界面。无论是管理网站、应用程序还是其他类型的后台操作,都可以使用Angular后台管理系统来简化开发和提高效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yanyi24

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值