逃坑威龙:来时无声,去时留踪
最近踩了几个不常见的坑,人虽然没emo,但是填坑那几天的精气神到底是有些不足。
这些天的经历过于丰富,一时间想不到语言去形容。所以我画了幅图,并取了个好听的名字——”逃坑威龙”。
虽然不排除,今后还会经历踩坑、填坑、再入新坑的循环。至少,这个短暂的时期内,我是处于写”万字感言”的阶段。
于是有了前一篇代码设计闭坑思路,也有了今天这篇数据权限的思考。
数据权限:可算是触发隐藏副本了
从权限控制说起吧
最近购物节,张三购买了不少日用品,当他打开自己的订单页面的时候,发现了一条不认识的购买记录,细看是李四的。
好家伙,这买的是什么,没想到你是这样的李四。
正常这种情况是不会出现的,因为开发者会做数据权限设计:
用户A只能访问到属于自己拥有的权限下的数据。
虽然看上去只有一句话,很好理解,好像很简单。但是实际情况,系统的权限层级不是单一的,随着层级的增加,不同层级的权限会出现交叉、包含等情况。
举个例子:
A战区业务员新建了几个活动,A战区的其他业务员能看到,而B战区的业务员是无法查看的。但是第一大区的经理是能看到第一大区下的A、B、C三个战区所有的数据的。
使用RBAC模型就可以实现基于角色的访问权限控制。
但是,实际业务场景中,除了角色还有更细小的权限控制,所以常规的权限控制的规则设计无法覆盖全。
隐藏副本:数据导出
做完权限管理,我遇到的第一个隐藏副本(掉坑),就是数据导出。
我们导出的那套系统是公共的,跟权限系统是分开的。
在没有任何特殊处理的情况下,某个角色是可以导出所有角色数据的。
原来不止人类的悲喜,系统也是不相通的。
后来,我在导出操作的方法中加入了当前角色的信息,才做到了数据的隔离。
数据导出的时候,除了需要传入筛选项的值,还需要默认带上用户的信息。
/**
* 数据导出公共方法
* @param {Object} 搜索项筛选数据对象
*/
const export = (searchParams) => {
const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
let params = {
...searchParams,
...userInfo.
}
}
<Button type="primary" onClick={() => util.export({ ...listRef.current.searchParams() })} >
导出
</Button>
当初就应该多看它几眼。
隐藏副本:不受控的”手动挡”
角色权限控制确实好用,但是也不是”万金油”。这不,我又触发了一次隐藏副本(掉坑)。
接续上面提到的活动的场景
每个战区新增的活动,都可以分配部分优惠券,这个是由大区经理操作的。战区业务员可以看到所在战区的活动绑定的优惠券。
这个功能比较简单,只需要对应的活动ID筛选绑定的优惠券。
然而战区业务员小明将优惠券页面的地址所携带的活动ID删掉之后刷新页面,看到了全部的优惠券数据。
他凌乱了。
我也凌乱了。
表格数据权限的兜底
权限控制不了”手动挡”,但是前端可以。
虽然有些后知后觉,但是我登时就想到了两种兜底方案。
方案一:表格组件增加必须入参的校验
该方案很适合:已经二次封装了表格组件。
我们在引入了第三方UI库之后,会使用UI库提供的表格组件,节省开发成本。
其实,还可以根据实际情况进行二次封装,二次封装的内容包含搜索项、导出操作、表格展示、表格数据请求等。
如下截图,大部分的表格页面基本都是类似的功能结构:
通用表格组件:List
1、组件增加了两个入参:
- requireFlag:是否校验必须入参的布尔值,默认为false,兼容所有不需要校验的页面。
- requireValue:必须入参的值,无默认值。
2、当符合需要校验必须参数且参数值为空时,表格数据设置为空数组。
// requireFlag的值默认为false
const { http, requireFlag = false, requireValue, pageParams } = props;
/**
* 表格数据查询方法
*/
const query = () => {
// =>true: 如果需要校验必须参数且参数值为空时,则表格数据为空的数组值
if (requireFlag && !requireValue) {
setList([]);
} else {
const params = { ...searchParams, ...pageParams, page, size };
http(params).then(res => {
setList(res.list)
});
}
};
活动管理页
引入组件之后,传入requireFlag和requireValue的实际值。
import { List } from '@/components';
<List http={getList} requireFlag='true' requireValue={record.id} pageParams={pageParams} />;
方案二:新增必须入参校验的组件
这个方案是通用的,无论前面有没有封装表格组件,都可以用方案二。
表格数据权限通用组件
表格回显的区分:
- 必须参数值不存在,展示空表;
- 必须参数值存在,展示正确的数据。
/**
* @description 表格数据脱敏业务组件
*/
import React from 'react';
import PropTypes from 'prop-types';
import { Table } from 'antd';
const CommonTableDataDesensitize = ({ data, children }) => {
const { columns, requiredVal } = data;
return <div>{requiredVal ? <>{children}</> : <Table dataSource={[]} columns={columns} rowKey={record => record.id} pagination={false} size="small" />}</div>;
};
CommonTableDataDesensitize.propTypes = {
data: PropTypes.object.isRequired, // 组件入参 必传
};
CommonTableDataDesensitize.defaultProps = {
data: {},
};
export default CommonTableDataDesensitize;
活动管理页
引入组件之后,将脱敏组件包裹在正确的数据内容外层并传入表格项的值和必须参数的值即可。
import { CommonTableDataDesensitize } from '@/Components';
......
<CommonTableDataDesensitize data={{ columns: columns, requiredVal: '必须参数的值' }}>
<Table columns={columns}/>
</CommonTableDataDesensitize>
以不变应万变
数据权限没有做全做好的问题,在功能没有出现bug前,是不好感知的,最好能想个法子提前进行规避。
我突然想到,之前看到《破事精英2》中的某一集,莫菲教金秘书用三连问的方式直击别人的灵魂。
于是,我思考三连问在开发中的可行性。连续追问,可以加深大脑对当时环境的印象,也可帮助后续产生相关性的联想。
所以,每次遇到数据权限类的功能呢,都先问自己三个问题:
- 数据的权限做了吗?
- 情况都考虑完全了吗?
- 需不需要做个兜底?
这样一来,可能之后都会养成思考在前的好习惯。
原文链接:https://juejin.cn/post/7239742600550203450 作者:叶一一