如题近段时间 需要在wpf应用中设计一个权限控制 , 简而言之的说 你懂的 对于IT人员来说都知道的 常见的软件功能 首先要有用户 用户,然后用户属于哪个角色 ,然后各个角色都有自己的可供操作的一堆功能,当然还有其它的复杂的控制方式 我并不想弄 只搞这种比较通用的。

首先是权限管理界面 以及数据操作 一堆功能的实现 比如 添加角色 设置权限,这个其实没啥好说的 就像你做传统的winform或者web一样 搞界面 访问数据库 做功能, 就是按部就班 。好吧 看下我用到的业务数据处理方法吧:

 1 public class UserLogic
2 {
3 internal UserInfo GetUserById(int id)
4 { }
5
6 internal void AddOrSaveUser(UserInfo user)
7 { }
8 internal List<UserInfo> GetDBUsers()
9 { }
10
11 internal List<RoleInfo> GetRoles()
12 { }
13
14 internal List<AuthorizationInfo> GetAllAuths()
15 { }
16
17 internal RoleInfo GetRoleByID(int id)
18 { }
19
20 internal List<AuthorizationInfo> GetAuthsByRoleID(int rid)
21 { }
22
23 internal void SetAuthsByRoleID(int rid,List<EAuthorizationItem> auths)
24 { }
25
26 internal int AddOrSaveRole(RoleInfo ro)
27 { }
28
29 internal void DelRole(int id)
30 { }
31
32 internal RoleInfo GetRoleByName(string name)
33 { }
34 }

删除角色:

 1 internal void DelRole(int id)
2 {
3 using (MyContext db = new MyContext())
4 {
5 var existR = db.roles.FirstOrDefault(r => r.ID == id);
6 db.roles.Remove(existR);
7
8 var auths = db.auths.Where(r => r.RoleID == id).ToList();
9 db.auths.RemoveRange(auths);
10
11 //已经用到了此角色 的用户 改为默认角色
12 var users = db.users.Where(r => r.RoleID == id).ToList();
13 var defRole = db.roles.FirstOrDefault(r => r.Name == nameof(RoleNameDefine.User));
14 for (int i = 0; i < users.Count; i++)
15 {
16 users[i].RoleID = defRole.ID;
17 }
18
19 db.SaveChanges();
20 }
21 }

设置权限:

 1 internal void SetAuthsByRoleID(int rid,List<EAuthorizationItem> auths)
2 {
3 using (MyContext db = new MyContext())
4 {
5 var existAuths= db.auths.Where(r=>r.RoleID==rid).ToList();
6 db.auths.RemoveRange(existAuths);
7
8 List<AuthorizationInfo> besave = new List<AuthorizationInfo>();
9 for (int i = 0; i < auths.Count; i++)
10 {
11 besave.Add(new AuthorizationInfo()
12 {
13 RoleID = rid,
14 AuthE = auths[i]
15 });
16 }
17
18 db.auths.AddRange(besave);
19 db.SaveChanges();
20 }
21 }

关于最终界面的样子嘛,也没美化就这样:

然后就是 特定操作的 权限  ,“权限” 这个东西我们以什么方式来描述  ,说白了就是 固定的字符串 比如"Add_xxInfo" "Del_xxInfo" ,再怎么我们的系统还是比较小 属于比较保守的 ,说白了就那么几个功能。不可能敞着 ,我们还是得以固定代码的方式定义这些描述  要不字符串 要不枚举。由于我自己借鉴了一种方式 可以比较方便的 完成 枚举数据 从代码 到数据库  以及界面显示 的交换。最终经过反复斟酌 我们还是选用了枚举:

 1 public enum EAuthorizationItem
2 {
3 [EnumDescription("打印机或自助机信息更新")]
4 PrinterOrTerminalUpdate,
5 [EnumDescription("打印机或自助机删除")]
6 PrinterOrTerminalDel,
7 [EnumDescription("数据接口管理")]
8 DataSourceMgt,
9 [EnumDescription("用户信息删除")]
10 UserDel,
11 [EnumDescription("用户信息更新")]
12 UserUpdate,
13 [EnumDescription("权限管理")]
14 RoleMgt
15 }

接下来的思路也是顺水推舟:
登录的时候 就能够确定所拥有的所有权限 生成功能标识数组,在登录结果里返回到客户端 ,客户端功能界面处 传入功能标识参数 通过一个统一的入口 与登录信息里的功能标识 数组 匹配 进而确定界面此部分功能是否启用。web那一套都熟悉 我们都知道怎么做,说起来简单 其实是琢磨了好久的,这是wpf。 首先要形成统一入口,不能到处编写权限判断代码 否则就违背我们的初衷了 哪怕复制粘贴同样的也不行, 我是用的mvvm方式 的, 如果我要做的话直接在viewModel里面 编写权限判断代码 很简单 毫无难度。然后另一个 可以绑定command 他可以通过canexecute 来影响界面是否可用 ,也是不错的方式 ,但是我由于一些特殊的原因 不能使用此方式。

说道此处最显而易见的都知道了 IsEnable=”{binding}“ ,特别说一下 通过此次的使用 让我对wpf的binding 有了一个更清晰的理解,binding 几大要素 ,source 数据源 没有指定source的时候默认以当前dataContext 一级一级的向上找 ,这也是我们使用mvvm的基本支撑。然后 还有 path ,绑定方向 和 converter不用多说了。为了这玩意儿我们也是煞费苦心。首先确定的是binding 必须要用binding ,我们要用的就是它自动化计算的功能 ,什么时候自动化计算 稍后再说。为了绑定功能标识传入参数 ,于是我们首先想到从 source入手 让其定位到一个static的东西 好处有二 ,首先static的 在一个地方统一编写就行了统一引用 维护方便不易出错,第二个有编辑提示 也就是.能.出东西来 比你硬编码字符串 不只是好点吧点。binding不都是动态值吗 我们此处却都是一个固定值 这感觉怪怪的,不要怪。我们上面说了利用他的动态计算功能 ,此处可以说明了 那就是converter ,通过熟读wpf 绑定原理过程 观察它走的路线你就会知道 最终是通过converter暴露的,对我们就在此处进行截获 。对功能标识参数与当前用户进行匹配 进而决定界面是否可用。说实话前面的你可以认为是传进来的已知参数。

好 看看我们的绑定

<MenuItem Header="编辑所选项" Name="me_Update" Click="me_Update_Click" IsEnabled="{Binding  UserUpdate ,Source={x:Static cd:AuthorizationItemDefine.Default},Converter={StaticResource auCOnverter}  }"></MenuItem>

来复习下wpf的绑定原理 source是让其定位到一个静态变量 而不是当前自动分配的datacontext, 然后绑定到里面的RoleMgt属性。Source={x:Static 这个是wpf设计很nice的地方 ,我们通过一个static的静态变量 但是类是new出来的 也就是单例模式,到处绑定 。

静态绑定定义:

 1 public class AuthorizationItemDefine : PropertyChangedBase
2 {
3 public static AuthorizationItemDefine Default { get { return m_Default; } }
4 private static AuthorizationItemDefine m_Default = new AuthorizationItemDefine();
5 AuthorizationItemDefine()
6 {
7 }
8
9 public void RiseProperty()
10 {
11 OnPropertyChanged(() => PrinterOrTerminalUpdate);
12 OnPropertyChanged(() => PrinterOrTerminalDel);
13 OnPropertyChanged(() => DataSourceMgt);
14 OnPropertyChanged(() => UserDel);
15 OnPropertyChanged(() => UserUpdate);
16 OnPropertyChanged(() => RoleMgt);
17 }
18 public EAuthorizationItem PrinterOrTerminalUpdate
19 {
20 get
21 { return EAuthorizationItem.PrinterOrTerminalUpdate; }
22 }
23 public EAuthorizationItem PrinterOrTerminalDel
24 {
25 get
26 { return EAuthorizationItem.PrinterOrTerminalDel; }
27 }
28 public EAuthorizationItem DataSourceMgt
29 {
30 get
31 { return EAuthorizationItem.DataSourceMgt; }
32 }
33
34 public EAuthorizationItem UserDel
35 {
36 get
37 { return EAuthorizationItem.UserDel; }
38 }
39 public EAuthorizationItem UserUpdate
40 {
41 get
42 { return EAuthorizationItem.UserUpdate; }
43 }
44 public EAuthorizationItem RoleMgt
45 {
46 get
47 { return EAuthorizationItem.RoleMgt; }
48 }
49 }

页面需要引入,以及定义converter:

1 xmlns:cd="clr-namespace:Common.Define;assembly=Common"
2 xmlns:cc="clr-namespace:AutoPrintClient"
3 <UserControl.Resources>
4 <cc:AuthConverter x:Key="auCOnverter"/>
5 </UserControl.Resources>

转换器很简单,就是看登录信息里有无对应的功能标识:

 1 class AuthConverter : IValueConverter
2 {
3 public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
4 {
5 if (value == null)
6 return false;
7 if (Runtime.Default.loginInfo == null || Runtime.Default.loginInfo.Auths == null)
8 return false;
9 string machAu = value.ToString();
10 if (Runtime.Default.loginInfo.Auths.Contains(machAu))
11 {
12 return true;
13 }
14 else
15 {
16 return false;
17 }
18 }
19
20 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
21 {
22 throw new NotImplementedException();
23 }
24 }

我们在用户登录之处就已经把功能标识数组 附在返回的登录信息里(Auths):

 1 //用户登录
2 var user = db.users.FirstOrDefault(r => r.LoginName == loginName && r.Password == password);
3 if (user != null)
4 {
5 RoleInfo ptrU = db.roles.FirstOrDefault(r => r.ID == user.RoleID);
6 log = new LoginInfo();
7 log.LoginName = loginName;
8 log.Password = password;
9 log.LoginAt = DateTime.Now;
10 if (ptrU != null)
11 {
12 log.RoleID = ptrU.ID;
13 log.RoleName = ptrU.Name;
14 log.RoleDescription = ptrU.Description;
15
16 var auths= db.auths.Where(r => r.RoleID == log.RoleID).ToList();
17 for (int i = 0; i < auths.Count; i++)
18 {
19 log.Auths.Add(auths[i].AuthE.ToString());
20 }
21 }
22 }
23 else
24 {
25 errorMsg = "数据库未找到对应用户记录";
26 }

问题又来了 ,何时进行更新

测试发现界面一直没有更新置灰 ,最后我们跟踪发现原来是usercontrol一出现的时候 就通过converter完成了binding ,而这时候其实我们还没有登录,而converter又是一个很特殊的玩意儿。我们是无法代码手动去触发他的,通过复习binding过程 推断 还是只得从值本身出发 , 这样converter就会触发了,去更新这个"其实是一直不变"的值 是不是一种很诡异的感觉 哈哈哈哈哈哈。。通过以前的知识我们知道 onPropertyChange 会触发依赖属性更新界面 。好咧 那就是他了 我们在前面的代码里加上RiseProperty方法 在里面刷新所有属性。其实上面已经是完整形式的代码了 ,就是上面贴出来的RiseProperty()方法这里就不贴了。思路顺水推舟 我们接下来做的自然是在 登录时进行 权限刷新 各处的界面刷新,通过与上面的结合 真是神来之笔。

登录刷新调用代码:

1 private void Click_login(object sender, RoutedEventArgs e)
2 {
3 if (vm.Login(passwordBox.Password) == true)
4 {
5 dlg_login.Visibility = Visibility.Hidden;
6 this.uct_Onlines.LoadFirstPage();
7 AuthorizationItemDefine.Default.RiseProperty();
8 }
9 }

最终的不同用户登录效果:

测试发现只需再登录成功后统一刷新一下就可以了 ,各处都会 按设想的工作 ,完美。干净手段解决问题的方式 你会发现  真的 真的 真的 很爽。

 

WPF应用中一种比较完美的权限控制设计方式的更多相关文章

  1. java中4种修饰符访问权限的区别及详解全过程

    java中4种修饰符访问权限的区别及详解全过程 http://jingyan.baidu.com/article/fedf0737700b3335ac8977ca.html java中4中修饰符分别为 ...

  2. Java中几种office文档转pdf的方式

    最近公司要做office的文档,搜集了几种office文档转pdf的方式,简单的做下总结 我主要尝试了三种方式:openoffice,aspose,jacob 对他们进行了大文件,小文件,在linux ...

  3. vue中如何实现后台管理系统的权限控制

    vuejs单页应用的权限管理实践 一.前言 在广告机项目中,角色的权限管理是卡了挺久的一个难点.首先我们确定的权限控制分为两大部分,其中根据粒的大小分的更细: 接口访问的权限控制 页面的权限控制 菜单 ...

  4. 在.NET Core中三种实现“可插拔”AOP编程方式(附源码)

    一看标题肯定会联想到使用动态编织的方式实现AOP编程,不过这不是作者本文讨论的重点. 本文讨论另外三种在netcore中可实现的方式,Filter(过滤器,严格意义上它算是AOP方式),Dynamic ...

  5. java中4种修饰符访问权限的区别

    访问权限 类 本包 子类 其他包 public √ √ √ √ protected √ √ √ x default(缺省) √ √ x x private √ x x x

  6. 三种SpringSecurity方法级别权限控制

    一 JSR-250注解 1.在pom.xml添加 <dependency> <groupId>javax.annotation</groupId> <arti ...

  7. asp.net core mvc权限控制:在视图中控制操作权限

    在asp.net core mvc中提供了权限验证框架,前面的文章中已经介绍了如何进行权限控制配置,权限配置好后,权限验证逻辑自动就会执行,但是在某些情况下,我们可能需要在代码里或者视图中通过手工方式 ...

  8. Android中的安全与访问权限控制

    Android是一个多进程系统,在这个系统中,应用程序(或者系统的部分)会在自己的进程中运行.系统和应用之间的安全性是通过Linux的facilities(工具,功能)在进程级别来强制实现的,比如会给 ...

  9. 两种RBAC权限控制模型详解

    序言 由于最近一直卡在权限控制这个坎上,原来设计的比较简单的权限控制思路已经无法满足比较复杂一些的场景,因此一直在探索一种在大部分场景下比较通用的权限模型. 首先,这里说明一下两种RBAC权限模型分别 ...

随机推荐

  1. Eating Peach (peach)

    Description On this day, the little monkey went looking for food. He came to a rectangular peach gar ...

  2. V4L2摄像头应用编程(转)

    Video for Linuxtwo(Video4Linux2)简称V4L2,是V4L的改进版.V4L2是linux操作系统下用于采集图片.视频和音频数据的API接口,配合适当的视频采集设备和相应的驱 ...

  3. rs232转以太网

    rs232转以太网 rs232转以太网ZLAN5103可以实现RS232/485/422和TCP/IP之间进行透明数据转发.方便地使得串口设备连接到以太网和Internet,实现串口设备的网络化升级. ...

  4. day26 Pyhton 面向对象复习

    一 class 类名(): pass 对象 object 对象 = 类名() class Person: pass print(Person)#<class '__main__.Person'& ...

  5. k8s-获取kuboardtoken

    master节点执行命令 echo $(kubectl -n kube-system get secret $(kubectl -n kube-system get secret | grep kub ...

  6. rabbitmq 交换机模式 -主题模式 topic

    建立一个交换机 tpc 并且绑定了各自的路由到 Q1 Q2 <?php require_once "./vendor/autoload.php"; use PhpAmqpLi ...

  7. spring boot:使用多个线程池实现实现任务的线程池隔离(spring boot 2.3.2)

    一,为什么要使用多个线程池? 使用多个线程池,把相同的任务放到同一个线程池中,可以起到隔离的作用,避免有线程出错时影响到其他线程池,例如只有一个线程池时,有两种任务,下单,处理图片,如果线程池被处理图 ...

  8. FreeRTOS链表实现

    直接上源码分析 void vListInitialise( List_t * const pxList ){ pxList->pxIndex = ( ListItem_t * ) &( ...

  9. 团体程序设计天梯赛-练习集 L1-007 念数字

    - - ->博主推荐,学生党.程序员必备,点击查看- - - >>>>> 热门文章推荐 以下50道算法编程题访问量较大,包含常用语法,数据结构,解题思路等等,作为C ...

  10. 在CentOS 8 上 部署 .Net Core 应用程序

    在Centos 8 上 部署 .Net Core 应用程序     -- 记录篇 1.更新dnf 源 1 dnf update 2.安装 Asp.Net Core 运行时 1 dnf install ...