【权限的思考】(一)使用反射实现动态权限
每一个业务系统都会根据业务需要配置各种各样的权限,实现方式也是千差万别,各有各的优缺点。今天我们
利用反射来做一个小的权限管理Demo。也可以说是插件化的权限管理,通用的插件化框架是实现一个接口或者协定,
我们的做法是先展示指定的数据,再去动态的加载需要用到的dll和功能。
大致的思路是这样的,我们从服务或者从数据库里读取哪些dll需要加载,相应的dll下哪些页面可以调用。把这些内容
动态的添加到页面上,当点击页面上的元素时利用反射,匹配目录下的dll和dll内的页面,进行读取,并显示进行交互,
从面实现插件化动态加载内容。如下图所示:
例:
》.首先我们先建一个工程ReflectionPermissionDemo
再添加3个类库ReflectionPermissionDemo.A;ReflectionPermissionDemo.B;ReflectionPermissionDemo.C;
需要注意的是不要在ReflectionPermissionDemo引用后边新建好的3个类库。我们的目标是用反射去加载这些dll库,
而不是使用引用方式。
创建完后还需要修改他们的输出路径属性
ReflectionPermissionDemo就生成到bin目录下
其它ReflectionPermissionDemo.A;ReflectionPermissionDemo.B;ReflectionPermissionDemo.C三个都指向这个
目录下生成。这样的目的是让所有的dll都在同一个文件夹下。
》.创建一些页面
ReflectionPermissionDemo项目里修改一下页面。在顶部添加一个Panel用来动态添加按钮,这些动态添加上的按钮就是我们利
用反射动态加载的dll库。在下边也添加一个Panel用于动态显示权限信息,对应是dll库里面有权限的页面。设置他们的
性Dock一个为Top下边的为Fill。并给他们命名上边的panelTop。叫下边的叫panelBody
在ReflectionPermissionDemo.A;ReflectionPermissionDemo.B;ReflectionPermissionDemo.C各自创建一些页面,这些页面也
需要权限设置的允许才能打开。
在每个页面上放一个标识,标明这个窗体与其它的窗体不同,可以根据个人需要,我这里放的是label写了一些文字标明每个窗体是
哪个项目的哪个窗体,如下图:
》模拟权限数据
写一个单例类,创建一些权限数据,用于模拟从服务器上返回的数据。返回的数据有模块id,模块名称,命名空间和父id。
我们可以把这些数结构想像成一棵树结构。
/// <summary>
/// 模拟远程服务器
/// 返回拥有的权限
/// </summary>
public class RemoteService
{
public static readonly RemoteService PermissionService = new RemoteService();
public DataTable PermissionTable { get; private set; }
#region 字段名称
public readonly string ModuleId = @"ModuleID";
public readonly string ModuleName = @"ModuleName";
public readonly string PermissioniNameSpace = @"PermissioniNameSpace";
public readonly string ParentId = @"ParentID";
#endregion
private RemoteService()
{
AllPermision();
}
/// <summary>
/// 所有的权限
/// </summary>
/// <returns></returns>
private DataTable AllPermision()
{
PermissionTable = new DataTable();
#region Permission Page
PermissionTable.Columns.AddRange(new[]
{
new DataColumn(ModuleId, typeof (Int32)), // 模块id
new DataColumn(ModuleName, typeof (string)), // 模块名称
new DataColumn(PermissioniNameSpace, typeof (string)), // 命名空间
new DataColumn(ParentId, typeof (Int32)) // 父id
});
#endregion
#region A
CreateNewRow(1001, @"A模块", @"A", -1);
CreateNewRow(1002, @"A 页面1", @"A.AForm1", 1001);
// 测试权限先注掉
//CreateNewRow(1003, @"A 页面2", @"A.AForm2", 1001);
#endregion
#region B 由于我们的例子只不需要B的权限,这里先注掉
//CreateNewRow(2001, @"B模块", @"B", -1);
//CreateNewRow(2002, @"B 页面1", @"B.BForm1", 2001);
#endregion
#region C
CreateNewRow(3001, @"C模块", @"C", -1);
CreateNewRow(3002, @"C Page1", @"C.CForm1", 3001);
CreateNewRow(3003, @"C Page2", @"C.CForm2", 3001);
#endregion
return null;
}
/// <summary>
/// 添加行
/// </summary>
/// <param name="moduleId"></param>
/// <param name="moduleName"></param>
/// <param name="perNameSpace"></param>
/// <param name="parentId"></param>
private void CreateNewRow(int moduleId, string moduleName, string perNameSpace, int parentId)
{
var newRow = PermissionTable.NewRow();
newRow[ModuleId] = moduleId;
newRow[ModuleName] = moduleName;
newRow[PermissioniNameSpace] = perNameSpace;
newRow[ParentId] = parentId;
PermissionTable.Rows.Add(newRow);
}
}
》根据权限动态加载页面和利用反射去打开相应的页面
先整理一下思路。
1.读取模拟的权限数据。
2.根据权限数据组织页面元素
》有哪些dll可以被加载
》相应的dll内有哪些页面可以被调用
3.点击页面元素根据保存的数据利用反射把页面展示出来。
——————————————————
>先添加一些字段。
// 权限数据
readonly DataTable _permissionDt = RemoteService.PermissionService.PermissionTable;
// 可用权限动态生成的panel页面
readonly Dictionary<int, FlowLayoutPanel> _pagePanels = new Dictionary<int, FlowLayoutPanel>();
// 已加载过的页面
private readonly Dictionary<string, Type> _formTypes = new Dictionary<string, Type>();
>根据权限数据动态组织页面
private void Main_Load(object sender, EventArgs e)
{
var query = _permissionDt.Rows.Cast<DataRow>();
var parentData = query.Where(x => int.Parse(x[RemoteService.PermissionService.ParentId].ToString()) == -1);
SettingDllButtons(parentData);
}
/// <summary>
/// 展示 dll的权限按钮
/// </summary>
/// <param name="dt"></param>
private void SettingDllButtons(IEnumerable<DataRow> drs)
{
int width = 80, height = 30, x = 0, y = 0;
foreach (var dataRow in drs)
{
var btn = new Button
{
Text = dataRow[RemoteService.PermissionService.ModuleName].ToString(),
Size = new Size(width, height),
Location = new Point(x, y)
};
var index = SettingPageButtons(dataRow);
btn.Tag = index;
btn.Click += btnDLL_Click;
panelTop.Controls.Add(btn);
x += width + 10;
}
}
/// <summary>
/// 根据 datarow的父id去找到所有的子节点
/// 加载到相应的页面上组织成按钮
/// </summary>
/// <param name="dr"></param>
/// <returns></returns>
private int SettingPageButtons(DataRow dr)
{
var index = _pagePanels.Count();
var panel = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
Location = new Point(0, 0),
Visible = false
};
panelBody.Controls.Add(panel);
_pagePanels[index] = panel;
#region Btns
var query = _permissionDt.Rows.Cast<DataRow>();
var data =
query.Where(
x =>
int.Parse(x[RemoteService.PermissionService.ParentId].ToString()) ==
int.Parse(dr[RemoteService.PermissionService.ModuleId].ToString()));
if (!data.Any())
return index;
int width = 80, height = 30;
foreach (var dataRow in data)
{
var btn = new Button
{
Text = dataRow[RemoteService.PermissionService.ModuleName].ToString(),
Size = new Size(width, height),
Tag = dataRow[RemoteService.PermissionService.PermissioniNameSpace]
};
btn.Click += btnPage_Click;
panel.Controls.Add(btn);
}
#endregion
return index;
}
> 利用点击不同的元素展示相应的页面
/// <summary>
/// 显示相应的页面元素
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnDLL_Click(object sender, EventArgs e)
{
var index = int.Parse(((Button) sender).Tag.ToString());
foreach (KeyValuePair<int, FlowLayoutPanel> flowLayoutPanel in _pagePanels)
{
flowLayoutPanel.Value.Visible = flowLayoutPanel.Key == index;
}
}
/// <summary>
/// 打开相应的page
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnPage_Click(object sender, EventArgs e)
{
var name = ((Button) sender).Tag.ToString();
var form = GetModule(name) as Form;
form.ShowDialog();
}
#region
/// <summary>
/// 利用反射去加载相应的页面
/// </summary>
/// <param name="path"></param>
/// <param name="mainNamespace"></param>
/// <returns></returns>
private object GetModule(string path, string mainNamespace = "ReflectionPermissionDemo")
{
var curNamespace = "";
var index = path.IndexOf('.');
if (index > -1)
{
curNamespace = "." + path.Substring(0, index);
}
else
{
curNamespace = "";
}
var assemblyPath = mainNamespace + curNamespace;
var classPath = mainNamespace + "." + path;
object module = null;
if (_formTypes.ContainsKey(classPath))
{
module = Activator.CreateInstance(_formTypes[classPath]);
}
else
{
try
{
module = Assembly.Load(assemblyPath).CreateInstance(classPath);
if (module != null)
_formTypes.Add(classPath, module.GetType());
}
catch
{
// 查找当前已加载的dll。
Type type = null;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (!assembly.FullName.Contains(mainNamespace))
continue;
type = assembly.GetType(classPath, false);
if (type != null)
{
break;
}
}
if (type == null)
{
throw;
}
else
{
module = Activator.CreateInstance(type);
if (module != null)
{
_formTypes.Add(classPath, type);
}
;
}
}
}
return module;
}
#endregion
跑一下,看一下效果
源码 : ReflectionPermissionDemo.zip
github 地址: https://github.com/lpxxn/ReflectionPermissionDemo
- jQuery VS JavaScript原生API
- 居于H5的多文件、大文件、多线程上传解决方案
- 抛弃websocket,前端直接打通信道,webRTC搭建音视频聊天
- Golang学习-第三篇 认识Web框架
- Golang学习-第二篇 搭建一个简单的Go Web服务器
- 数据说话:Go语言的Switch和Map性能实测
- Dora.Interception, 为.NET Core度身打造的AOP框架[4]:演示几个典型应用
- Dora.Interception, 为.NET Core度身打造的AOP框架[3]:Interceptor的注册
- Dora.Interception, 为.NET Core度身打造的AOP框架:不一样的Interceptor定义方式
- Dora.Interception,为.NET Core度身打造的AOP框架:全新的版本
- ASP.NET Core的路由[4]:来认识一下实现路由的RouterMiddleware中间件
- 浅谈 Java 并发编程中的若干核心技术
- ASP.NET Core的路由[3]:Router的创建者——RouteBuilder
- ASP.NET Core的路由[2]:路由系统的核心对象——Router
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Linux中利用Vim对文件进行密码保护的方法详解
- Linux中禁止用户修改/重置密码
- 详解 MAC/Linux Vi配置环境变量及Java环境变量配置
- 扒开DMA映射的内裤
- 如何理解与识别Linux中的文件类型
- ubantu 16.4下Hadoop完全分布式搭建实战教程
- Linux定时删除日志的简单实现方法
- ubuntu如何安装中文输入法
- Ubuntu18.04安装Python虚拟环境
- linux下查看so或可执行程序的依赖库
- Linux中crontab输出重定向不生效问题的解决方法
- Ubuntu中安装MySQL更改默认密码的步骤详解
- linux下用csplit命令分割文件的方法示例
- Linux基础命令之mktemp详解
- CentOS7设置jar应用程序开机启动的方法