C#如何优雅的对WinForm窗体应用程序进行权限控制
作者:阔活洵信
前言
特别复杂特别高大上的系统我还没有机会接触,就我了解的来看,普通的功能权限控制的流程都差不多只有两个过程:
- 获取当前用户拥有的权限
- 在界面上对功能入库的可用性或者可见性进行控制
这里说一种在WinForm窗体应用开发时进行权限控制的办法,文章中主要针对上述两个过程的第二步。不过为了说清楚,我先简单说一下我的数据库功能表设计。
数据库
大家的权限数据库好像都差不多,我比较习惯Code First,所以就直接贴数据库对应的对象。记录下功能名称,所属模块,窗体名称,控件名称。权限表里面主要就是记录了用户(角色)ID和功能的对应了,有记录就表示该用户(角色)拥有该功能权限,没有记录就是没有权限。
/// <summary> /// 系统功能 /// </summary> [SugarTable("X_FUNCTION", "系统功能")] public class FUNCTION { [SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键", Length = 50)] public string GUID { get; set; } /// <summary> /// 功能名称 /// </summary> [SugarColumn(ColumnDataType = "NVARCHAR2(30)", ColumnDescription = "功能名称")] public string NAME { get; set; } /// <summary> /// 模块ID /// </summary> [SugarColumn(ColumnDescription = "模块ID", Length = 50)] public string MODULEID { get; set; } /// <summary> /// 窗体名称 /// </summary> [SugarColumn(ColumnDescription = "窗体名称", Length = 50)] public string FORMNAME { get; set; } /// <summary> /// 控件名称 /// </summary> [SugarColumn(ColumnDescription = "控件名称", Length = 50)] public string CONTROLNAME { get; set; } }
如何控制
最简单的方法,就是自己在代码里面把每个功能入口的控件都手动判断一遍,他们的name在不在有权限的功能列表的控件名称里。当然简单很有可能就代表麻烦,有的窗口可能要控制的上百控件,得写上百if-esle,这实在是太不优雅了,如果还有多个窗口的权限需要控制,那就真是灾难了。这个时候反射就可以起到比较好的作用。我们可以创建一个继承自Form的类,在这个类里面检查权限并设置控件是否可用。我使用了DevExpress控件,所以继承自XtraForm。然后只要窗口继承CheckPowerXtraForm,就可以进行权限控制了。
代码中有两个地方需要注意,第一是要等待创建窗口句柄再设置窗口控件的可用性。因为要等待,以及读数据库,所以不能放在主线程里面(写到这里,忽然觉得这个检查权限应该放在FormLoad事件里比较好,不用Thread.Sleep,不过要读数据库,还是不适合放在主线程)。
/// <summary> /// 检查权限 /// </summary> public class CheckPowerXtraForm : XtraForm { public CheckPowerXtraForm() { //不要再主线程运行 Task.Run(() => { CheckPower(); }); } /// <summary> /// 检查权限 /// </summary> private async void CheckPower() { var powerDal = new PowerDAL(); var functions = await powerDal.GetPowerFunctionsByUserAsync(Global.User.GUID); //获取值属于本窗体的权限控件名称 var formControls = functions.Where(f => f.FORMNAME == this.GetType().Name).Select(f => f.CONTROLNAME).ToList(); //等待创建窗口句柄 while (!this.Visible) { Thread.Sleep(100); } this.BeginInvoke(new Action(()=>{ var type = this.GetType(); var fields = this.GetType().GetFields(BindingFlags.NonPublic|BindingFlags.Instance); foreach (var field in fields) { var value = field.GetValue(this); if (value is Control) { var control = value as Control; if (!formControls.Contains(control.Name)) { control.Enabled = false; } } else if (value is BarItem) { var item = value as BarItem; if (!formControls.Contains(item.Name)) { item.Enabled = false; } } else if (value is ToolStripMenuItem) { var item = value as ToolStripMenuItem; if (!formControls.Contains(item.Name)) { item.Enabled = false; } } } })); } }
问题
上面的做法有一个问题,窗口里面不是所有控件都需要权限控制的,有的是一些容器性的控件比如Groupbox,有的是一些是个用户就有的功能比如退出登录。按上面代码的做法,只要没有记录的,全部都会设置成不可用。所以我们需要对需要权限控制的做一个标记。有两个办法:
- 把所有需要控制权限的控件Enabled都事先设置成false,然后有权限的就设置成true。
- 给需要控制的控件设置一个特性。
下面说说第二种做法。首先定义一个特性,里面什么都不需要写。
/// <summary> /// 检查权限的特性 /// </summary> [AttributeUsage(AttributeTargets.Field)] public class CheckPowerAttribute : Attribute { }
然后给需要控制的控件赋予这个特性。
[CheckPowerAttribute] private DevExpress.XtraBars.BarButtonItem mbbi_exportexcel; [CheckPowerAttribute] private DevExpress.XtraBars.BarButtonItem mbbi_exportcad; [CheckPowerAttribute] private DevExpress.XtraBars.BarButtonItem mbbi_exportshp;
然后在权限控制代码里只处理具有这个特性的控件。
//~~~ foreach (var field in fields) { var cattributes = field.GetCustomAttributes(typeof(CheckPowerAttribute), false); if (cattributes.Length != 0) { var value = field.GetValue(this); if (value is Control) { var control = value as Control; if (!formControls.Contains(control.Name)) { control.Enabled = false; } } else if (value is BarItem) { var item = value as BarItem; if (!formControls.Contains(item.Name)) { item.Enabled = false; } } else if (value is ToolStripMenuItem) { var item = value as ToolStripMenuItem; if (!formControls.Contains(item.Name)) { item.Enabled = false; } } } } //~~~
总结
说是优雅,其实自己一边写一边都觉得有不少改进的地方,比如CheckPower()执行的位置,还有设置Enable的时候用反射设置也会简洁很多,如果有更好的改进方案欢迎评论讨论。
到此这篇关于C#如何优雅的对WinForm窗体应用程序进行权限控制的文章就介绍到这了,更多相关C# WinForm窗体应用权限控制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!