源代码GitHub:https://github.com/ZhaoRd/Zrd_0001_AuthorityManagement
1.介绍
对于权限管理系统来说,系统模块是必须的一部分,那么如何处理和收集模块信息是一个管家步骤,在没有看郭民峰的osharp之前,我能想到的都是通过管理员通过后台管理进行管理,osharp里采用attirbute的方式采集模块信息。该demo借鉴osharp的方式,通过使用attribute定义模块信息,程序启动时通过判断attribute进行模块和具体功能权限的信息收集。
对于新采集的信息,如何判定这些新信息哪些是需要更新的,哪些是需要从已有信息中删除的,哪些又是需要新增的呢?这里需要使用到集合运算,具体请看以下内容。
2.模块信息的定义
在web中,操作一个功能的访问路径是 controller/action ,在本demo中,一个controller就是一个功能,一个action就是一个具体的操作权限。在源代码中,定义SystemModelAttribute来定义功能信息,代码如下:
/// <summary>
/// 收集系统模块.
/// </summary>
public class SystemModelAttribute : Attribute
{
/// <summary>
/// 模块名称.
/// </summary>
private string name;
/// <summary>
/// 分组名称.
/// </summary>
private string groupName;
/// <summary>
/// 只读模块名称.
/// </summary>
public string Name {
get
{
return this.name;
}
}
/// <summary>
/// 分组名称.
/// </summary>
public string GroupName {
get
{
return this.groupName;
}
set
{
// 如果是通过构造函数初始化的名称,则其他设置均无效
if (string.IsNullOrEmpty(this.groupName))
{
this.groupName = value;
}
}
}
/// <summary>
/// 模块的权限.
/// </summary>
private PermissionValue permissionValue;
/// <summary>
/// 模块的权限.
/// </summary>
public PermissionValue PermissionValue {
get
{
return this.permissionValue;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="SystemModelAttribute"/> class.
/// </summary>
/// <param name="name">
/// 模块名称.
/// </param>
/// <param name="permissionValue">
/// 模块权限值.
/// </param>
/// <param name="groupName">
/// 分组名称.
/// </param>
public SystemModelAttribute(string name, PermissionValue permissionValue = PermissionValue.All, string groupName = null)
{
this.name = name;
this.groupName = groupName;
this.permissionValue = permissionValue;
}
}
在controller上使用该attribute,即可定义一个模块,使用方式如下图
SystemModelAttribute可以定义需要采集的模块信息,同理,可以在定义一个attribute,来定义需要采集的权限信息,源代码如下
/// <summary>
/// 权限信息设置,以便信息收集.
/// </summary>
public class PermissionSettingAttribute : Attribute
{
/// <summary>
/// 具体权限.
/// </summary>
private readonly PermissionValue permissionValue;
/// <summary>
/// Initializes a new instance of the <see cref="PermissionSettingAttribute"/> class.
/// </summary>
/// <param name="value">
/// The value.
/// </param>
public PermissionSettingAttribute(PermissionValue value)
{
this.permissionValue = value;
}
/// <summary>
/// Gets the permission value.
/// </summary>
public PermissionValue PermissionValue
{
get
{
return this.permissionValue;
}
}
}
使用方式如下:
通过使用SystemModelAttribute和PermissionSettingAttribute来定义需要采集信息的内容.
3.信息采集
我们在Controller和Action上分别使用了不同的Attribute来定义信息,如何在代码中收集这些信息呢?主要代码如下
首先是获取到Controller所在的程序集,然后根据获得的类型处理Type,具体的Invoke方法如下
/// <summary>
/// 解析Controller类型来收集Attribute信息.
/// </summary>
/// <param name="target">
/// The target.
/// </param>
/// <returns>
/// The <see cref="IEnumerable"/>.
/// </returns>
public IEnumerable<FunctionDto> Invoke(Type target)
{
var result = new List<FunctionDto>();
var targetType = target;
// 是否使用SystemModelAttribute
if (!targetType.IsDefined(typeof(SystemModelAttribute), false))
{
return result;
}
// 获取所有Controller里的方法
var methods = targetType.GetMethods();
// 获取功能模块的具体权限,必须是使用了PermissionSettingAttribute定义权限值,并取所有功能的并运算
// 例如:一个Controller里只定义了Create和Edit,那么这个功能模块的权限就是 Create|Edit
var functionPermissionValue =
(from methodInfo in methods
where methodInfo.IsDefined(typeof(PermissionSettingAttribute), false)
select methodInfo.GetCustomAttribute<PermissionSettingAttribute>()).Aggregate(
PermissionValue.None,
(current, permissionSetting) => current | permissionSetting.PermissionValue);
// 获取SystemModelAttribute具体的信息
var description = targetType.GetCustomAttribute<SystemModelAttribute>();
var areaName = this.GetArea(target);
var function = new FunctionDto()
{
FunctionName = description.Name,
ModelName = areaName,
PermissionValue = functionPermissionValue
};
result.Add(function);
return result;
}
通过这样的一个函数,就可以采集到一个Controller里的权限信息,一个功能就是一个Controller,Controller里使用PermissionSettingAttribute定义的具体操作权限,所有定义的权限值取并运算,
如图所示,一个Controller里包含Create和Edit,那么就说明这个功能模块的最大权限是Create|Edit,这样,在进行权限分配的时候,只能对该功能所具有的权限进行分配,未具有的权限不能分配.
4. 信息处理
经过上面的处理,那么功能信息和权限信息都已经采集到了,将这些信息保存到数据库,即可快捷方便的初始化系统的功能信息。
在信息处理方面,主要使用了集合运算,为了对集合运算进行说明,我把已存在数据库里的功能信息集合定义为 A集合,新采集的集合定义为 B集合,那么对于B集合而言,怎么判断哪些是需要新添加到数据库,哪些是需要更新到数据库的,已存在数据库里的功能,哪些又是需要删除的呢?
a.对于需要添加的功能,可以理解为存在B集合但是不存在A集合
b.对于需要更新的功能,可以理解为即存在B集合又存在A集合
c.对于需要删除的功能,可以理解为存在A集合中但是不存在B集合中
从集合的角度来处理a b c三种情况,那么
a. B集合 减 A集合
b. B集合 交 A集合
c. A集合 减 B集合
通过集合运算,就可以得到需要进行添加、更新、删除的功能信息了
代码如下:
/// <summary>
/// 初始化系统功能.
/// </summary>
/// <param name="functionDtos">
/// The function dtos.
/// </param>
public void InitModel(IEnumerable<FunctionDto> functionDtos)
{
var functions = this.functionRepository.FindAll().ToList();
var addFunctions = functionDtos.Select(Mapper.Map<FunctionDto, Function>)
.AsEnumerable();
// 创建如何判断两个function是否相等的条件
var functionComparer = EqualityHelper<Function>.CreateComparer(m => m.ModelName + "#" + m.FunctionName);
var enumerable = addFunctions as Function[] ?? addFunctions.ToArray();
// 包含在将要处理的集合(addFunctions)
// 但不包含在已经存在的集合(functions)
// 表示需要添加到系统里的模块
// 差集运算
var toAddFunctions = enumerable.Except(functions, functionComparer);
// 包含在已经存在的集合(functions)
// 但不包含在将要处理的集合(addFunctions)
// 表示需要从系统中删除的模块
// 差集运算
var toDeleteFunctions = functions.Except(enumerable, functionComparer);
// 即包含在将要处理的集合(addFunctions)
// 又包含在已经存在的集合(functions)
// 表示需要更新内容
// 交集运算
var toUpdateFunctions = functions.Intersect(enumerable, functionComparer);
LogHelper.Logger.Info(
string.Format(
"新增功能:{0}条;更新功能:{1}条;删除功能:{2};",
toAddFunctions.Count(),
toUpdateFunctions.Count(),
toDeleteFunctions.Count()));
foreach (var addFunction in toAddFunctions)
{
addFunction.ID = Guid.NewGuid();
var role = this.roleRepository.Find(
Specification<Role>.Eval(u => u.RoleName == "系统管理员"));
this.functionRepository.Add(addFunction);
// 初始化系统管理员的权限
this.functionInRoleRepository.Add(new FunctionInRole()
{
ID = GuidHelper.GenerateGuid(),
Function = addFunction,
PermissionValue = addFunction.PermissionValue,
Role = role
});
}
foreach (var deleteFunction in toDeleteFunctions)
{
this.functionRepository.Remove(deleteFunction);
}
foreach (var updateFunction in toUpdateFunctions)
{
var function = updateFunction;
var query = enumerable.Where(m => m.FunctionName == function.FunctionName);
var newValue = string.IsNullOrEmpty(updateFunction.ModelName) ? query.SingleOrDefault(u => u.ModelName == null) : query.SingleOrDefault(u => u.ModelName == function.ModelName);
if (newValue == null)
{
continue;
}
updateFunction.FunctionName = newValue.FunctionName;
updateFunction.ActionName = newValue.ActionName;
updateFunction.AreasName = newValue.AreasName;
updateFunction.ControllerName = newValue.ControllerName;
updateFunction.Description = newValue.Description;
updateFunction.ModelName = newValue.ModelName;
updateFunction.PermissionValue = newValue.PermissionValue;
this.functionRepository.Update(updateFunction);
}
this.functionInRoleRepository.Context.Commit();
}
5. 总结
信息的定义和收集,主要是使用Attribute,该demo提供了一种使用Attribute的使用方式和方法。
该篇还提供了一个使用集合进行处理数据的方式,扩展了我再处理集合时的一些思路,以前总是通过for 循环来处理两个集合,那么通过集合运算,可以很方便的得到集合信息。
推荐QQ群:
278252889(AngularJS中文社区)
5008599(MVC EF交流群)
134710707(ABP架构设计交流群 )
59557329(c#基地 )
230516560(.NET DDD基地 )
本人联系方式:QQ:351157970