如题近段时间 需要在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. Java源码详解系列(十一)--Spring的使用和源码

    Spring 是一个一站式的 Java 框架,致力于提高我们项目开发的效率.通过 Spring,我们可以避免编写大量额外代码,更专注于我们的核心逻辑.目前,Spring 已经成为最受欢迎的 Java ...

  2. 感觉学java学到自己的瓶颈期了,各种框架乱七八糟,感觉好乱。该怎么办!?

    通常我们都会有这样的一个疑问! 解决办法 这时候,你需要的是分清条理,重整知识架构 GitHub开源社区有一个这样的项目,我觉得非常好,很适合Java有基础但是想进阶提升的人. 项目简介 本期介绍的开 ...

  3. Linux桌面环境配置

    目录 更换软件源 中文输入法 firefox安装flash插件 编译安装Vim 关闭蓝牙开机自启 yakuake无法正常使用 在中文环境下将默认目录修改成英文 电脑换成了thinkpad x1c 20 ...

  4. 多测师讲解自动化测试 _RF分配id_高级讲师肖sir

    1.Assign Id To Element.

  5. 从源码角度来分析线程池-ThreadPoolExecutor实现原理

    作为一名Java开发工程师,想必性能问题是不可避免的.通常,在遇到性能瓶颈时第一时间肯定会想到利用缓存来解决问题,然而缓存虽好用,但也并非万能,某些场景依然无法覆盖.比如:需要实时.多次调用第三方AP ...

  6. IIdea使用CXF开发WebService

    写这篇文章主要是用于增强记忆,而我参考的是这位朋友的随笔,链接如下 http://www.xiaomager.com/415.html 服务端开发过程 1.首先创建一个maven项目,如下图 2.添加 ...

  7. 在shell中截取心仪的字符串

    file=/dir1/dir2/dir3/my.file.txt ${file#*/} 去掉左边 ${file##*/} 去掉左边最后一个 ${file%/*} 去掉右边 ${file%%/*} 去掉 ...

  8. 【C语言入门学习笔记】如何把C语言程序变成可执行文件!

    环境 在ANSI的任何一种实现中,存在两种不同的环境. 翻译环境:在这个环境里,源代码被转换为可执行的机器指令. 执行环境:用于实际执行代码. 翻译环境 组成一个程序的每个源文件通过编译过程分别转成目 ...

  9. Windows下使用GitStack搭建Git服务器

    Win10下使用GitStack搭建Git服务器 Git是目前世界上最先进的分布式版本控制系统(没有之一). ​ 许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别. ...

  10. docker启动服务---------------rabbitmq

    1.进入docker hub镜像仓库地址:https://hub.docker.com/ 2.搜索rabbitMq,进入官方的镜像,可以看到以下几种类型的镜像:我们选择带有"mangemen ...