在我的很多Winform开发项目中,统一采用了权限管理模块来进行各种权限的控制,包括常规的功能权限(工具栏、按钮、菜单权限),另外还可以进行字段级别的字段权限控制,字段权限是我们在一些对权限要求比较严格的系统里面涉及到的,可以对部分用户隐藏一些敏感的信息,或者禁止不够权限的用户编辑它。本篇随笔介绍基于这一理念,实现在WxPython跨平台开发框架中的模块字段权限的管理。
1、字段权限的设计
字段的权限控制,一般就是控制对应角色人员的对某个模块的一些敏感字段的可访问性:包括可见、可编辑性等处理。
在设计字段权限的时候,我们需要了解这些也是基于RBAC的概念,基于角色进行授权的,而且我们的字段列表是属于具体的业务对象列表的,这里的业务对象是指一些我们具体的业务模块,如客户基础信息、人员基础信息、报价单等等,我们就是基于这些业务进行字段的控制的。
为了实现对业务模型的字段控制,我们在数据库中设计两个数据库,一个用于存储对应实体类名称的信息,如ID,实体类全名,类路径等主体信息;一个存储对应角色配置的字段列表信息,结合起来就可以实现对应角色的字段权限控制了,数据库表设计信息如下所示。
如下界面所示,我们在权限系统里面也可以对其字段进行权限控制,如下图所示。先选择左边的具体角色,然后添加一些业务对象,并设置它们的权限即可。
首次业务对象需要用户加入,这里以wxpython前端框架的实体类进行字段信息的标识处理,如下所示可以加载对应业务信息。
确认后,系统记录对应角色对业务模块中的相关字段的权限处理。
我们在业务对象列表的【显示设置】处可以单击旁边的按钮,在弹出的界面上进行条件的设置,如下界面效果所示。
这样我们就完成了对某个业务对象的各个字段进行配置了,具体的字段控制在业务模块里面添加部分代码即可实现了。
2、业务模块对字段控制的处理
如下面列表界面,对于隐藏的字段,统一进行***符号的遮挡处理,即使导出Excel数据,结果也是***替代。
同时,如果系统界面有新增或者编辑界面,那么我们也需要隐藏才可以达到效果,如下是其的编辑界面效果(隐藏显示年龄字段了)。
如果仅仅是禁止编辑,那么我们配置角色对该字段不允许编辑即可。
那么用户对应客户信息模块的年龄字段可以看到,但是不能编辑。
3、字段权限的列表控制处理
前面我们介绍了在权限系统中进行业务对象的字段权限的设置流程,以及以其中的【客户信息】的业务模块进行的演示,那么我们如何才能在自己的业务模块里面进行控制处理的呢?
首先我们需要在业务列表绑定的时候,需要获取我们当前用户能够访问的字段列表,默认是全部可见,但是如果用户设置了条件,那么就需要获取对应的权限列表进行控制了,具体的控制代码如下所示。
在我的WxPython跨平台开发框架中,我们对于常规窗体列表界面做了抽象处理,一般绝大多数的逻辑封装在基类上,基类提供一些可重写的函数给子类实现弹性化的处理。
有了基类的挡箭牌,我们就可以统一在上面进行列表界面中字段权限的判断处理了。
我们在基类窗体的类上,对更新表格数据的处理进行调整,增加对字段权限的获取,在传递给实际更新表格的接口即可,如下代码所示。
由于在窗体基类,可以获得对泛型类型的一些属性,因此我们也就直接获得该对象的字段权限的配置了,如下代码所示。
async def get_columns_permit(self) -> dict[str, int]:
"""获取当前用户对该模块的列权限字典"""
# 1、获得对象的模块路径+类名
entityFullName = f"{self.model.__module__}.{self.model.__name__}"
# 2、获取当前用户对该模块的列权限字典
result = await api_fieldpermit.get_columns_permit(
entityFullName, settings.CurrentUser.id
)
return result
因为对客户信息的实体类,我们在数据库的表中配置的记录如下所示。
根据实体类全称和字段名称,以及对应该用户的角色ID,我们就能唯一确定该字段的权限了。
因此我们在自定义的表格数据对象中,设置返回的单元格值进行控制处理即可进行脱敏处理了。
由于我们的表格数据默认就是只读的,因此只需要判断隐藏的进行脱敏处理即可。
通过上面的控制,就可以实现列表中对字段的统一控制处理了。对于不同的业务表对象,都不需要单独的进行设置,已经在基类统一处理相关的逻辑了。
3、字段权限的显示窗体控制处理
如果在开发界面的时候,把列表的展示统一放在wx.Grid列表控件进行展示,不再独立设计展示窗体,那么上面列表控制就已经达到了字段权限的控制目的了:可见或不可见、可编辑或只读等处理。
在我的wxpython开发框架中,我一般倾向于设计一个界面来展示业务对象的内容,一般新增,查看或者编辑都放在这个窗体上展示信息,比较直观,那么这种对字段权限的控制也需要延伸到这个显示窗体上;
对于普通的编辑控件,我们只能控制控件的可读写、可见与否的处理。为了避免重复对字段权限的请求,我们在列表界面请求获得的字段权限列表,可以传递给新增、编辑对话框界面,如下代码是在子类列表界面,打开新增、编辑对话框的时候进行的处理。
async def OnEditById(self, entity_id: Any | str):
"""子类重写-根据主键值打开编辑对话框"""
# 使用列表窗体获得的字段权限
dlg = FrmCustomerEdit(self, entity_id, columns_permit=self.columns_permit)
# if dlg.ShowModal() == wx.ID_OK:
if await AsyncShowDialogModal(dlg) == wx.ID_OK:
# 更新grid列表数据
await self.update_grid()
dlg.Destroy()
可以看到,已经对字段权限的字典对象进行了传递,方便使用,避免重复请求了。
一般常规的新增、编辑界面,我们对它们也是进行了基类的封装处理,以便对界面元素进行更好的统一控制。
详细的基类设计如下所示,如对于客户信息的新增、编辑对话框,关系图如下所示。
由于新增/编辑对话框中,往往使用我们自定义的控件进行处理,因此在自定义控件中,可以再初始化函数的时候,添加一个 permit 的属性字段,然后进行判断控件的处理即可。
# 字段权限,默认0表示不限制,1表示只读,2 隐藏
self.permit = permit
if permit == 2:
self.Hide() # 隐藏
elif permit == 1:
self.ReadOny() # 只读
else:
pass # 不限制
而对于非自定义的控件,我们就需要再外部初始化的时候,对它们进行一些额外的处理控制显示了。
age_permit = self.columns_permit.get("age", 0)
self.txtAge = wx.SpinCtrl(panel, permit=age_permit)
if age_permit == 2:
self.txtAge.Hide() # 隐藏
elif age_permit == 1:
self.txtAge.Disable() # 只读
那么用户对应客户信息模块的年龄字段可以看到,但是不能编辑。
由于新增、编辑界面是属于子类完全自定义控件和展示的逻辑,因此无法在基类统一进行处理,但是可以通过自定义控件的方式,减少过多的干预处理。
对于列表界面,则是 可以进行统一的字段的控制展示,比较一致。
以上就是字段权限的设计思路,实现控制过程,这样我们在权限里面实现了功能权限、菜单权限、数据记录权限、字段权限的综合控制,基本上能够满足大多数业务规则的要求了,从而提高了权限管理系统在整个应用开发中的通用性、便利性,一致性。