Winform仿WEB权限
Winform里做权限控制,十有八九是拿个TreeView勾选菜单,再塞几个bool字段。这种玩法对付小工具还行,一旦菜单超过两级、角色超过三个,代码就开始发臭——if套if,改一处崩三处。我见过最离谱的项目,权限判断直接写在按钮的Click事件里,四十个窗体复制粘贴同一段逻辑,后来产品说要加个“临时访客”角色,开发组改了整整两周,还漏了两个弹窗没拦住。
后来被逼急了,索性照搬WEB那套思路。前端菜单不直接绑定窗体,改成从数据库读一棵权限树,每个节点存一个唯一编码,比如“system.user.add”这种点分格式。后端拦截器在窗体Load之前先跑一遍,拿当前用户的角色去查这棵树的交集,能匹配到编码才允许实例化。说白了,就是把WEB的Filter或中间件那套逻辑,硬塞进Winform的消息循环里。
具体做法其实不复杂。先建一张角色表、一张权限点表,再加一张关联表。权限点就是那棵树的扁平化映射,父节点用ParentCode关联。登录成功后,一次性把当前用户的权限编码Load进一个HashSet,全局静态存着。每个窗体在构造时调用一个静态方法,传自己的窗体编码,比如“frm_sale_order”,方法内部检查这个编码在不在HashSet里,不在就抛异常或者直接return null。
关键细节在按钮级别。窗体能打开只是第一步,里面的增删改查按钮还得单独控制。这时候别在窗体里写死按钮的Enabled,而是给每个按钮的Tag属性塞一个权限编码。窗体Load完后遍历所有控件,凡是Tag不为空的,就去HashSet里查,没有就禁用。这套机制跑通后,新加一个角色只需要在数据库里勾选权限点,代码一行都不用动。
有个坑必须提醒——Winform的窗体缓存。WEB是无状态的,每次请求都重新过Filter,但Winform里如果你用单例窗体,第一次打开时权限校验过了,后面用户角色变了,单例还活着,权限就失效了。所以要么别用单例,要么在每次Show之前强制重新校验一次。我自己的做法是写了个OpenForm的静态工具方法,每次先查权限再决定是new一个实例还是直接用缓存。
这么改完,最大的好处是权限逻辑从业务代码里彻底剥出去了。有一次客户提需求,说销售部的经理能看到成本价,但普通销售只能看到零售价。以前这得在十几个销售相关的窗体里加if,现在只需要在权限树里加一个“sale.view_cost”的权限点,然后窗体里那个显示成本的Label绑定这个编码,数据层根据权限过滤字段,完事。测试的时候直接换个账号登录,点两下就验证完,不用再去翻代码找哪个窗体漏了判断。
当然也有矫枉过正的时候。有些内部工具总共就五个人用,也非要用这套树形权限,结果光维护权限点就比写业务还累。我的建议是,角色超过三个,或者菜单层级超过两层,才值得上这套。否则老老实实写个bool字段,比什么都强。权限这东西,本质是给“不信任”准备的,五个人互相都知根知底,搞那么复杂纯属自虐。
最后说个观察——很多人觉得Winform是老古董,但实际做企业内部系统,它比WEB顺手得多。WEB那套权限模型成熟,是因为有HTTP的无状态特性逼着你把每次请求都当独立事件处理。Winform想借鉴,难点不在技术,在于把那个“每次请求都校验”的习惯移植过来,别仗着窗体是常驻内存的就偷懒。真把这习惯养成了,你会发现权限代码反而比WEB还好维护,至少不用跟Session过期较劲。
(责任编辑:综合)













