• 作者:老汪软件技巧
  • 发表时间:2024-09-20 15:01
  • 浏览量:

一、简介

权限控制不仅是一个必不可少的功能,也关乎系统的安全性和用户体验。无论是小型应用还是企业级系统,针对不同用户角色精细化地管理权限至关重要。然而,对于很多开发者特别是新手来说,复杂的权限体系可能让人摸不着头脑。本文将通过简单的方式,带你了解前端权限控制的几种常见方案及它们的优缺点,帮助你更好地为项目选择合适的方案,并解决开发中常遇到的一些问题。

二、介绍常见的权限控制模型基于角色的访问控制(RBAC) :通过角色管理权限,用户被分配一个或多个角色,角色决定了用户能访问的资源和操作。简单易管理,适用于大多数系统。基于属性的访问控制(ABAC) :通过用户、资源、环境等属性动态决定权限,适合复杂且细粒度的权限需求,但实现和维护较复杂。权限树模型:以树状结构表示权限,节点层级明确,上层权限自动继承给下层,适合层级化权限管理,如文件系统或项目管理系统。三、前端权限控制的实现方案优缺点讲解

权限控制大多使用RBAC模型(基于角色的访问控制),在开发中,前端和后端对于权限控制的协作方式可以有所不同。以下是几种常见的方案:

1. 解析菜单访问控制的不同方案方案一 : 前端维护页面与角色映射 | 后端仅提供用户角色

描述

在这种方案中,所有页面和角色的对应关系都在前端定义,前端通过后端返回的用户角色信息来决定加载哪些页面或功能。前端会维护完整的角色-页面映射表,用户的角色权限由后端提供,前端根据该信息做权限判断和渲染。

案例

const routes = [
  { path: '/dashboard', component: Dashboard, roles: ['admin', 'user'] },
  { path: '/admin', component: AdminPanel, roles: ['admin'] }
];
const userRole = getUserRole(); // 后端返回的角色信息
const accessibleRoutes = routes.filter(route => route.roles.includes(userRole));
// 只渲染用户有权限访问的路由
router.addRoutes(accessibleRoutes);

优缺点

缺点:方案二 : 前端基于后端返回的权限数据直接渲染 | 后端仅返回用户可访问菜单

描述

在这种方案中,前端只负责渲染后端返回的菜单或功能项。后端根据用户角色或权限,过滤出用户有权限访问的菜单,直接返回给前端。前端不再需要做权限判断,只负责根据后端的数据动态渲染页面。

案例

// 后端返回的用户可访问的菜单
const userMenus = fetchUserMenus(); // 根据用户角色从后端获取菜单
// 直接渲染后端返回的菜单
renderMenu(userMenus);

缺点:

方案三 : 前后端混合权限控制 | 静态配置结合动态加载

实战文章链接 : /post/741565…

描述

目前比较常用,在某些系统中,前端会将一些静态的权限配置结合动态的后端权限控制。前端会加载部分静态的页面和功能项(例如,公开访问的页面),而后端则负责动态返回用户有权限访问的页面或操作。这种方式结合了前端灵活性和后端的安全性。

案例

// 静态路由配置
const staticRoutes = [
  { path: '/public', component: PublicPage },
];
// 动态从后端获取的用户权限路由
const dynamicRoutes = fetchUserRoutes(); // 从后端获取用户可访问的动态路由
// 合并静态和动态路由
const finalRoutes = [...staticRoutes, ...dynamicRoutes];
// 渲染路由
router.addRoutes(finalRoutes);

缺点:

2. 资源访问控制方案一: 前端自定义权限指令 | 结合后端角色与资源授权

描述

在这种方案中,前端自定义 Vue 指令,用于控制页面或组件的显示、功能按钮的启用等操作。后端负责提供资源的授权信息以及用户所属的角色,前端通过这些信息判断用户是否具有特定资源的访问权限。这种方式允许前端灵活地根据业务场景控制资源的可见性或可操作性。

案例

请求后端提供的授权信息

假设后端通过 API 返回了当前用户的角色和资源授权信息,例如:

{
  "userRole": "admin",
  "permissions": {
    "system:user:list",
    "system:user:save",
    "system:user:delete",
    "system:user:update",
    "..."
  }
}

前端自定义指令 v-permission

前端可以定义一个自定义权限指令,通过绑定后端返回的权限信息来控制组件的可见性或操作性。Vue 中可以通过v-permission指令来实现。(省略编写,理解意思即可)

// 定义自定义权限指令 v-permission
import Vue from 'vue';
// 权限数据通常来自后端接口,可以存储在 Vuex 或其他全局状态管理工具中
const userPermissions = {
  viewDashboard: true,
  editPost: false,
  deleteUser: true
};
Vue.directive('permission', {
  inserted(el, binding) {
    const permissionKey = binding.value; // 获取指令的值,即权限键
    if (!userPermissions[permissionKey]) {
      el.parentNode && el.parentNode.removeChild(el); // 如果没有权限,移除元素
    }
  }
});

通过 v-permission 控制页面元素

在 Vue 组件中使用v-permission来控制页面元素的渲染:

<template>
  <div>
    
    <button v-permission="'system:user:list'">查看仪表板button>
    
    <button v-permission="'system:user:delete'">删除用户button>
    
    <button v-permission="'system:user:update'">编辑帖子button>
  div>
template>

本地缓存权限信息减少对后端的依赖

前端通过发送请求,向后端获取当前用户的权限信息,并缓存在本地。

// 获取权限信息的 API 调用
fetch('/api/permissions')
  .then(response => response.json())
  .then(data => {
    store.commit('setPermissions', data.permissions); // 存储权限信息
  });

优点:

缺点:

方案二 : 权限的其他方案很少使用,不过多介绍四、 常见问题与解决方案

在前端权限控制的实现过程中,可能会遇到各种问题。以下列举了几类常见问题以及对应的解决方案:

性能问题

由于前端权限控制涉及到动态路由、菜单渲染和复杂的权限逻辑,尤其在处理大量角色、权限和页面时,性能问题可能会出现。常见的性能问题包括加载时间过长、界面卡顿等。

1. 懒加载路由和组件

对于不常用的页面或功能,使用 Vue 的懒加载(import())按需加载页面组件,减少初次加载的时间。

const AdminPanel = () => import('@/views/AdminPanel.vue');

2. 优化数据传输

对于菜单、权限数据,可以只传输当前用户可用的数据,而不是全部信息,避免前端处理无用数据导致的性能下降。

3. 缓存权限信息

可以将用户的权限数据缓存到本地(如Vuex、Redux、pinia),减少每次重新加载时的请求次数。(注:不建议存储在 localStorage 防止篡改)

权限更新的实时性 & 权限数据与前端视图不同步1. 实时通信

通过 轮询、Websocket、SSE 等方案实时让后端提供权限信息,前端接收到权限变更后立刻刷新权限数据。

2. 手动刷新机制(常用)

后端更新权限信息,在用户点击需要API访问按钮时,后端拦截访问( 注: 可在后端判定拦截后刷新页面 )。

前端权限被绕过

由于前端权限控制依赖于客户端逻辑,容易被有心人通过直接访问API进行请求,后端需要同样进行权限认证。

五、实战案例 & 源码

在实际开发中,尽管介绍了多种前端权限控制的实现方案,但往往会根据项目需求采用多种方案的混合方式。文章虽然提供了方案的核心代码和思路,但在实际编码时,逻辑通常会更加复杂。

源码 : /fateyifei/y… ( 注 : yf-vue-admin 为前端 )

实战文章 : /post/741565…