基于tailwind做前端元素权限控制,还行!

1,847 阅读6分钟

1 背景

1.1 权限控制

在不同颗粒度、纬度的区分下,可以分模块、页面、元素、接口、数据等

  • 当用户加载模块或者页面,可以基于用户的权限在网关和接口侧进行403拦截

  • 接口可以在后端进行拦截器处理

  • 元素可以基于前端用户当前拥有的最小权限颗粒度进行选择性展示或者交互逻辑

  • 数据权限,由接口测把控,控制前端查询、操作,公司内部sql执行器的权限

1.2 RABC模型

RBAC模型(Role-Based Access Control:基于角色的访问控制)模型是在项目中常用于权限控制的模型。

基础版本,可以分为三个部分,“用户” 、“角色”、“权限”,用户可以拥有多个角色,一个角色可以对应多个权限。

在实际项目中往往会有权限中心去做配置,一个权限会有一个“权限码”,权限在前端代码处理过程中会涉及两个部分

  1. 页面控制

  2. 如果是通过接口做的后端指向前端资源的前后端分离模式,后端会根据权限拦截返回页面是否,同时会返回有权限的菜单menu,对于菜单进行前端动态加载 / 前端判断

  3. 如果是单页面【spa】,前端路由通过前端控制的前后端分离模式,我们可以根据权限码对应的路由进行动态加载 / 或者直接展示

  4. 元素控制

  5. 当前页面所具有的权限【可能是全局、可能不是】,作用于当前页面的元素内容,也可能会分展示/交互

如图所示

1.3 tailwind

在目前前端tailwind对于涉及样式的UI库、项目等,愈发流行,对于这种原子化css的样式,市场已经给出了反馈,可以看下每年github的top库里面会用的依赖。

基于postcss,把样式能力聚焦在classname,在已经具有样式的规范约定之上,同样有拓展能力,可以看到社区也会有基于tailwind的插件,作用于富文本、表单、自适应等场景。

有记忆门槛,但是确实好用,不做过多陈述!

基于上面的应用场景和所需技术栈,如果针对于前端页面内元素基于rabc的展示控制,我想可以通过tailwind来做到这点。于是有了 tailwind-rabc www.npmjs.com/package/tai…


2 可以做什么

tailwind-rabc 可以基于样式的能力来控制前端元素的展示控制,举个🌰,之前我们做权限控制的时候

第一步:通过后端注入或者接口拿到权限码的集合,比如当前用户权限码集合

const auths = ['create', 'update']

表示当前用户具有查看创建和修改的权限

第二步:封装权限按钮,通过当前按钮标识和全局权限auths做比对

const AuthComp = (props: {auth: string}) => { .... }

第三步:引入权限按钮组件,在对应元素位置,把对应的code当作props传给权限按钮

<AuthComp auth="create">
    <div>xxx</div>
</AuthComp>

--------------------------- 以前👆  手动分割  现在👇---------------------------

如果是纯展示控制,其实把展示交给css就好

第一步: 配置权限合集,例如项目涉及到的权限

['create', 'update']

第二步:在对应元素写对应权限类

<div className="auth-create">xxx</div>

3 使用

前提是你的项目使用tailwind

3.1 下载安装

npm i tailwind-rabc

3.2 配置

tailwind的配置文件:tailwind.config.cjs

const rabc = require('tailwind-rabc');

module.exports = {  content: ['./src/**/*.{js,ts,jsx,tsx}'],  darkMode: ['selector', '[data-mode="dark"]'],  plugins: [    // require('@tailwindcss/typography'),    // require('@tailwindcss/aspect-ratio'),    // require('@tailwindcss/forms'),    rabc.plugin({      rabcCode: ['Admin', 'Developer', 'Watcher'],      mountSelector: 'div',    }),  ],};

参数:

rabcCode

当前项目涉及的权限码,也就是rabc模型中对应的“角色”和“权限”

当只有“权限”

如果只有权限的时候,直接传入数组,元素为权限系统生成的权限码,例如 权限吗 12、13

rabcCode: ['12', '13']

当有“角色” 和 “权限”

针对一些项目会有角色和权限同时生效的时候,比如某个元素是某类角色可以直接看到,如果使用权限颗粒度控制,会有大量的权限码比对的情况,

使用对象来配置,角色名做key,角色对应权限码集合做值

{
    [role: string]: auth[]
}

例如:

rabcCode: { manager: ['12', '13'], developer: ['13'] }

mountSelector

权限作用域的css选择器,可选,默认 body,这个是针对css选择器来选择是在当前元素上挂载当前用户所拥有的权限集合的元素选择器,使用场景如下

⚠️:这里的css选择器,后续注入code的元素是能够和这个css选择器匹配

  1. 如果是全局权限,没有因为具体页面会变动,可以挂在body上,就可以不填,默认body,或者自行选择#app,

  2. 如果是分页面,可以用页面最外面的元素作为css选择器,比如#page-a

  3. 当然如果是范式,可以写入div,这种比较宽泛,大多数人挂载权限选择的元素就是div

type

注入到元素后,选择挂载元素的类型,可选,可以 "class" | "data" 这个参数影响到挂载权限元素是通过什么方式挂载的。

3.3 注入权限

import { mountRabc } from "tailwind-rabc";
const ref = useRef<HTMLDivElement>(null);
// ...
// 这里元素需要和mountSelector 对应上
const yourAccountAuth = ["12"];
// const yourAccountAuth = { manager: ['12', '13']}; 如果只有一个role manager 可以跟一个空的[]
// 表示当前挂载到这个元素的子元素控制权限为 role: manager, 以及权限code为12,13
useEffect(() => {
  if (ref && ref.current) {
    mountRabc(yourAccountAuth, ref.current);
  }
}, [ref]);
// ...
return <div ref={ref}></div>;

这是生成对应的权限作用域,当前ref的子孙元素会对权限码生效

3.4 权限元素

a 当前元素拥有权限,代码写入

<div className="auth-12"></div>

可以基于tailwind的vscode提示🔔

对于只有“权限”的配置,例如只配置了 rabcCode: ['12', '13']

则可以对元素className为 auth-12 、auth-13 

对于“权限” 和 “角色”,例如 配置了 rabcCode: { manager: ['12', '13'], developer: ['13'] }

可以对元素className为 

角色:role-manager、role-developer

权限:auth-12、auth-13

b 当命中权限展示,需要该元素需要的展示类型,即该元素应当的 css属性:display

目前支持:

  'none',  'block',  'inline',  'inline-block',  'flex',  'inline-flex',  'grid',  'table',  'table-row',  'table-cell',

4 分析

优势:

1 该策略可以针对于可跨页面夸元素的元素权限展示控制,颗粒度可以进行划分,支持角色和权限共存

2 代码编写友好,对于样式注入、权限控制很清晰

3 基于tailwind的能力,代码提示完备,代码风格统一

4 权限控制,在代码干预编写中比较方便

劣势:

1 目前功能比较基于rabc的基础版本,对于多权限的控制比较不足

2 基于css【tailwind】,所以能力上限是css的上限,同时也依赖于项目使用tailwind

如果有其他对权限和tailwind的结合有比较好的想法、或者对于tailwind能力拓展有好的想法,可以评论下,友好讨论下。感谢!