尝试封装适用于权限管理的通用API

谈谈我对权限系统的简单理解

最近一段时间在研究权限系统,在园子里看到个很牛逼的开源的基于DDD-Lite的权限管理系统,并有幸加入了作者的QQ群,呵呵,受到了很大的影响。对于权限管理我有我自己的一些简单想法,说简单也许是因为我对权限管理领域的理解有限,我所做的仅是基于我本人对权限管理的理解。以最终目的为出发点,应该就是一个系统根据不同的登陆用户的不同权限而呈现不同的界面,不同的操作,数据和资源等等。从实现方面大体可以分几个部分:用户登陆管理部分,用户权限分配部分,响应终端或界面部分。目前我已经根据我自己的上述理解,最简单的实现了前两个部分。

看看我的框架是否合理

尝试封装适用于权限管理的通用API

我的Domain层主要分了两个部分,一个是RBAC,一个是原有的一些基础架构,RBAC是后面加进去的,完全可以是独立的一个库,不依赖任何第三方框架和数据库。主要功能就是为任何一个实体绑定权限或是资源,提供实体的权限查询和权限检测,这个实体不限于用户或角色

尝试封装适用于权限管理的通用API

这里其实有点过度了,在现有框架中接口完全可以无视的。因为这个RBAC不依赖于其它框架或其它层,它又在Domain层,其它层可以直接拿去用。

首先说说模型,有三个模基本模型,BaseEntity是基础实体模型,主要统一标识类型和设置id唯一属性;BaseRelevance是最关健的模型,用于记录一个实体对另一个实体或是权限或是资源的关联关系,对这个模型的所有属性除了ID其它都从代码逻辑中赋值。其中有一个属性对应了一个枚举类PermissionType,用于记录权限的类型。另一个BaseHideFiled用于实现某些实体的字段隐藏权限,主要记录要隐藏字段的实体的ID和字段名称。下面把代码贴出来大家看看

namespace YZY.Domain
{
    /// <summary>
    /// 权限类型
    /// </summary>
    public enum PermissionType
    {
        /// <summary>
        /// 管理员操作权限
        /// </summary>
        AdmainAction,
        /// <summary>
        /// 角色
        /// </summary>
        Role,
        /// <summary>
        /// 组资源
        /// </summary>
        Group,
        /// <summary>
        /// 功能模块或菜单
        /// </summary>
        Module,
        /// <summary>
        /// 操作
        /// </summary>
        Action,
        /// <summary>
        /// 按钮
        /// </summary>
        Button,
        /// <summary>
        /// 控制器
        /// </summary>
        Contorller,
        /// <summary>
        /// 属性
        /// </summary>
        Propety,
        /// <summary>
        /// 数据
        /// </summary>
        Data,
        /// <summary>
        /// 用户
        /// </summary>
        User,
        /// <summary>
        /// 其它
        /// </summary>
        Other
    }
}

枚举类

namespace YZY.Domain
{
    public abstract class BaseEntity<TKey> : IEntity<TKey>
    {
        #region 初始化
        public TKey Id { get; set; }
        protected BaseEntity(TKey id)
        {
            this.Id = id;
        }
        #endregion
    }

}

基类

namespace YZY.Domain
{
    public abstract class BaseRelevance<TKey>:BaseEntity<TKey>,IRelevance<TKey>
    {
        protected BaseRelevance(TKey id) : base(id) { }

        /// <summary>
        /// 权限类型
        /// </summary>
        public string PermissionType { get; set; }
        /// <summary>
        /// 实体的id
        /// </summary>
        public TKey EntityId { get; set; }
        /// <summary>
        /// 实体类型
        /// </summary>
        public string EntityType { get; set; }
        /// <summary>
        /// 权限id
        /// </summary>
        public TKey PermisId { get; set; }
    }
}

关联类

namespace YZY.Domain
{
    public abstract class BaseHideFiled<TKey>:BaseEntity<TKey>,IHideFiled<TKey>
    {
        protected BaseHideFiled(TKey id) : base(id)
        {

        }
        public TKey EntityId { get; set; }
        public string HidePropertyName { get; set; }
        public string EntityType { get; set; }

    }
}

隐藏字段类

基础功能都在RBACManage类中实现,由于没有依赖数据库,所以没有实体绑定权限持久化的具体实现,相关的方法返回一个关联类的类型实例,以供有数据库的层(比如服务层)持久化。呵呵,说服务层有数据库其实是不对的,因为服务层其实只是调用Domain层的接口提供的方法而已,具体怎么实现,它并不知道。这里顺便以领域层,服务层和数据库层关系说说我对分层和依赖注入的简单理解,Domain应该是根据业务需要设计出相应的逻辑实现,但是数据必须持久化,而对于数据库或是ORM框架是个可变化点,为了响应这个变化点而设计出专门针对数据库的接口,你可以以不同的ORM来实现这个接口,比如EF或NH或是ADO.NET。服务层中要用到Domain层的接口,但是必须要有个具体实现来操作,这时候就得用上依赖注入了。层之间的关系就变成了服务层引用Domain,调用它的接口,数据库层也引用Domain层,实现它的接口,服务层不引用数据库层却通过接口和注入间接调用了数据库层。RBACManage代码后面再奉上。这里主要先看看我的框架怎么样。

尝试封装适用于权限管理的通用API

基础服务中,UserManage主要负责对用户的基础管理,有几个静态方法供其它层调用,目前我只写了登陆管理(cookie+cache),密码加密解密,其它功能还没有写;CustomWarring是一个自定义异常类,主要方便异常管理。DependencyContext依赖注入上下文,通过这个类,可以在其它层使用不同的依赖注入组件,比如可以用autofac,也可以用其它。pager是我自己写的一个超简单却实用的分页类。其它一些接口加上依赖注入组件主要用于隔离层与层之间的依赖,或方便一个基础功能可以有不同的具体实现的变化点。模型目前主要是一些简单的权限管理模型,方便测试。对于框架的结构,这里不能讲太多,否则跑题了。如果有时间,或是大家觉得可参考,再搞一篇文章详细谈谈

试试我的权限操作类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace YZY.Domain
{

    /// <summary>
    ///  权限管理操作
    /// </summary>
    /// <typeparam name="TEntity">实体类</typeparam>
    /// <typeparam name="TPermission">权限类</typeparam>
    public class RBACManage<TEntity,TKey> : IRBACManage<TEntity, TKey>
        where TEntity : BaseEntity<TKey>
    {
        public  IQueryable<BaseRelevance<TKey>> RelevanceQuery { get; set; }
        public  IQueryable<BaseHideFiled<TKey>> HideFiledQuery { get; set; }

        /// <summary>
        /// 仓储检测
        /// </summary>
        private void CheckQuery()
        {
            if (RelevanceQuery == null)
                throw new Exception("没有对应的仓储");
        }        

        /// <summary>
        /// 获取一个实体绑定一个权限后的关联类
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="newRelvance"></param>
        /// <param name="permis"></param>
        /// <param name="perType"></param>
        /// <returns></returns>
        public BaseRelevance<TKey> GetBindRelevance<TPermission>
            (TEntity entity,
            BaseRelevance<TKey> newRelvance,
            TPermission permis,
            PermissionType perType)
            where TPermission : BaseEntity<TKey>
        {
            if (permis == null) return null;
            newRelvance.EntityId = entity.Id;
            newRelvance.EntityType = entity.GetType().Name;
            newRelvance.PermisId = permis.Id;
            newRelvance.PermissionType = perType.ToString();
            return newRelvance;
        }

        /// <summary>
        /// 将一个实体的某个字段注册成隐藏字段权限并返回这个权限类型
        /// </summary>
        /// <typeparam name="TFiled"></typeparam>
        /// <param name="entity"></param>
        /// <param name="filed"></param>
        /// <param name="newHide">new一个即可</param>
        /// <returns></returns>
        public BaseHideFiled<TKey> GetRegisteHideFiled<TFiled>
            (TEntity entity,Expression<Func<TEntity,TFiled>>filed,BaseHideFiled<TKey>newHide)
        {
            newHide.EntityId = entity.Id;
            newHide.HidePropertyName = LambdExt.GetName(filed);
            newHide.EntityType = entity.GetType().Name;
            return newHide;
        }
        /// <summary>
        /// 将一个实体的某个字段注册成隐藏字段权限并返回这个权限类型
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="filedName"></param>
        /// <param name="newHide"></param>
        /// <returns></returns>
        public BaseHideFiled<TKey> GetRegisteHideFiled(TEntity entity,string filedName,BaseHideFiled<TKey>newHide)
        {
            newHide.EntityId = entity.Id;
            newHide.HidePropertyName = filedName;
            newHide.EntityType = entity.GetType().Name;
            return newHide;
        }

        /// <summary>
        /// 将一个实体的隐藏字段权限绑定到其它实体的关联类型
        /// 比如可以将一个用户的某个字段对其它用户或角色隐藏
        /// </summary>
        /// <typeparam name="TFiled">字段类型</typeparam>
        /// <typeparam name="TOherEntity">其它实体类型</typeparam>
        /// <param name="newHide">new一个即可</param>
        /// <param name="entity">当前实体</param>
        /// <param name="newRelvance">new一个即可</param>
        /// <param name="otherEntitiy">其它实体</param>
        /// <param name="filed">字段名称</param>
        /// <returns></returns>
        public BaseRelevance<TKey> GetBindHideFiledToOtherEntityRelevance<TOherEntity, TFiled>
            (TEntity entity,
            BaseRelevance<TKey> newRelvance,
            TOherEntity otherEntitiy,
            Expression<Func<TEntity, TFiled>> filed)
            where TOherEntity : BaseEntity<TKey>
        {
            //从隐藏字段权限的集合中找出这个权限 ,如果不存在报异常
            BaseHideFiled<TKey> hide = null;
            try
            {
                hide = HideFiledQuery.ToList().Where
                        (o => o.EntityId.Equals(entity.Id) && o.HidePropertyName == LambdExt.GetName(filed)).First();
            }
            catch
            {
                throw new Exception("当前实体还没有注册指定的字段隐藏权限,请先将其注册成隐藏字段权限");
            }
            if (HideFiledQuery == null) throw new Exception("权限仓储为空");
            newRelvance.EntityId = otherEntitiy.Id;
            newRelvance.PermisId = hide.Id;
            newRelvance.EntityType = otherEntitiy.GetType().Name;
            newRelvance.PermissionType = PermissionType.Propety.ToString();                       

            return newRelvance;
        }

        /// <summary>
        /// 将一个实体的隐藏字段权限绑定到其它实体的关联类型
        /// 比如可以将一个用户的某个字段对其它用户或角色隐藏
        /// </summary>
        /// <typeparam name="TOther">其它实体类型</typeparam>
        /// <param name="entity">当前实体</param>
        /// <param name="newRelvance">new一个即可</param>
        /// <param name="other">其它实体</param>
        /// <param name="filedName">字段名称</param>
        /// <returns></returns>
        public BaseRelevance<TKey> GetBindHideFiledToOtherEntityRelevance<TOther>
            (TEntity entity,BaseRelevance<TKey>newRelvance,TOther other,string filedName)
            where TOther:BaseEntity<TKey>
        {
            //从隐藏字段权限的集合中找出这个权限 ,如果不存在报异常
            BaseHideFiled<TKey> hide = null;
            try
            {
                hide = HideFiledQuery.ToList().Where
                        (o => o.EntityId.Equals(entity.Id) && o.HidePropertyName == filedName).First();
            }
            catch
            {
                throw new Exception("当前实体还没有注册指定的字段隐藏权限,请先将其注册成隐藏字段权限");
            }
            if (HideFiledQuery == null) throw new Exception("权限仓储为空");
            newRelvance.EntityId = other.Id;
            newRelvance.PermisId = hide.Id;
            newRelvance.EntityType = other.GetType().Name;
            newRelvance.PermissionType = PermissionType.Propety.ToString();

            return newRelvance;
        }

        /// <summary>
        /// 检测一个实体是否有指定的权限
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="permis"></param>
        /// <returns></returns>
        public bool ExitsPermission<TPermission>
            (TEntity entity, TPermission permis) where TPermission : BaseEntity<TKey>
        {
            return RelevanceQuery.AsEnumerable().Any(o => o.EntityId.Equals(entity.Id) && o.PermisId.Equals(permis.Id));
        }

        /// <summary>
        /// 获取实体对应的权限集合
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<TPermission> GetPermissions<TPermission>
            (TEntity entity,
            IQueryable<TPermission> permisQuery,
            Func<BaseRelevance<TKey>,bool>relevanceFilter=null,
            Func<TPermission, bool> perFilter = null)
            where TPermission : BaseEntity<TKey>
        {
            var ids = FindPermKeys(entity,relevanceFilter);
            if (permisQuery == null) throw new Exception("权限仓储为空");
            var all = new List<TPermission>();
            ids.ToList().ForEach(o => all.Add(permisQuery.ToList().Where(e => e.Id.Equals(o)).First()));
            if (perFilter == null) return all;
            return all.Where(perFilter);
        }

        /// <summary>
        /// 获取实体对应的隐藏字段权限
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="hideFilter"></param>
        /// <returns></returns>
        public IEnumerable<BaseHideFiled<TKey>>GetHideFileds
            (TEntity entity,Func<BaseHideFiled<TKey>,bool>hideFilter=null)
        {
            var ids = FindPermKeys(entity, o => o.PermissionType == PermissionType.Propety.ToString());

            if (HideFiledQuery==null) throw new Exception("权限仓储为空");
            var all = new List<BaseHideFiled<TKey>>();
            ids.ToList().ForEach(o => all.Add(HideFiledQuery.ToList().Where(e => e.Id.Equals(o)).First()));
            if (hideFilter == null) return all;
            return all.Where(hideFilter);
        }

        /// <summary>
        /// 获取实体对应的全部权限id
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public IEnumerable<TKey> FindPermKeys(TEntity entity,Func<BaseRelevance<TKey>,bool>filter=null)
        {
            CheckQuery();
            var all= RelevanceQuery.ToList().Where(o => o.EntityId.Equals(entity.Id));
            if (filter != null) return all.Where(filter).Select(o => o.PermisId);
            return all.Select(o => o.PermisId);

        }

        /// <summary>
        /// 按权限类型获取实体的权限集合
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="type"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<TPermission> GetPermissionsForType<TPermission>
            (TEntity entity,
            PermissionType type,
            IQueryable<TPermission> PermisQuery,
            Func<TPermission, bool>filter = null)
            where TPermission : BaseEntity<TKey>
        {
            return GetPermissions(entity,PermisQuery, o => o.PermissionType == type.ToString(), filter);
        }            

        /// <summary>
        /// 找出实体对应隐藏字段权限指定类型集合
        /// 比找出一个用户或角色对应的所有隐藏字段的权限,然后将它们转成对应的隐藏字段后的实体
        /// </summary>
        /// <typeparam name="TOther">任意类型</typeparam>
        /// <param name="entity">当前实体</param>
        /// <param name="otherQuery"></param>
        /// <returns></returns>
        public IEnumerable<TOther> GetHideFiledPermissions<TOther>
            (TEntity entity, IQueryable<TOther> otherQuery)
            where TOther : BaseEntity<TKey>
        {

            //获取实体对应的所有隐藏字段类型的权限
            var hides = GetHideFileds(entity);

            //找出TOther类型的权限集合
            if (hides == null) return null;
            var others = hides.Where(o => o.EntityType == typeof(TOther).Name);

            //根据id进行分组
            var look = others.ToLookup(o => o.EntityId, o => o.HidePropertyName);
            List<TOther> list = new List<TOther>();
            //List<Guid> hideIds = new List<Guid>();
            foreach (var item in look)
            {
                //得到这个类型下的所有id
                var other = otherQuery.ToList().Where(o => o.Id.Equals(item.Key)).FirstOrDefault();

                List<string> proNames = new List<string>();
                foreach (var fi in item)
                {
                    proNames.Add(fi);
                }
                list.Add(PropertyValueHelper.GetChangeProValueToDefault(other, proNames));
            }
            return list;
        }
    }

}

哦,这里还用到了两个帮助类,一个是用于获取属性名,一个是用于更改对应属性名的值。其实也就是两个反射方法封装一下。

using System;
using System.Linq.Expressions;

namespace YZY.Domain
{
    public  class LambdExt
    {
        /// <summary>
        /// 获取成员名称,范例:t => t.Name,返回 Name
        /// </summary>
        /// <param name="expression">表达式,范例:t => t.Name</param>
        public static string GetName(LambdaExpression expression)
        {
            var memberExpression = GetMemberExpression(expression);
            if (memberExpression == null)
                return string.Empty;
            string result = memberExpression.ToString();
            );
        }

        /// <summary>
        /// 获取成员表达式
        /// </summary>
        private static MemberExpression GetMemberExpression(LambdaExpression expression)
        {
            if (expression == null)
                return null;
            var unaryExpression = expression.Body as UnaryExpression;
            if (unaryExpression == null)
                return expression.Body as MemberExpression;
            return unaryExpression.Operand as MemberExpression;
        }
    }
}

获取lambd表达式字段名

using System.Collections.Generic;
using System.Linq;

namespace YZY.Domain
{
    public class PropertyValueHelper
    {
        /// <summary>
        /// 改变实例的指定属性值为默认值,返回改变后的实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static T GetChangeProValueToDefault<T>(T obj,List<string> propertyNames)
        {
            foreach (var item in propertyNames)
            {
                var pro = obj.GetType().GetProperties().Where(o => o.Name == item).FirstOrDefault();
                pro.SetValue(obj, default(object));

            }
            return obj;
        }
    }
}

修改属性值

为了最小化类型参数,我搞了必须的两个属性,以方便服务层中传相应的数据仓储。添加绑定时返回关联实例类,用IQueryable<T>来代替T类型的数据仓储,因此脱离数据库操作。方便测试

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;

namespace YZY.Domain.Tests
{
    [TestClass()]
    public class RBACManageTests
    {
        public class UserRbac : RBACManage<User, Guid> { }
        public class RoleRbac : RBACManage<Role, Guid> { }
        void Initia()
        {
            ; i < ; i++)
            {
                User u = new User { Name = "u" + i, Telephone = "tel" + i };
                uList.Add(u);
            }
            ; i < ; i++)
            {
                Role r = new Role { Name = "r" + i };
                rList.Add(r);
            }
            rbacR = new RoleRbac();
            rbacR.RelevanceQuery = reList.AsQueryable();
            rbacR.HideFiledQuery = hList.AsQueryable();

            rbacU = new UserRbac();
            rbacU.HideFiledQuery = rbacR.HideFiledQuery;
            rbacU.RelevanceQuery = rbacR.RelevanceQuery;
        }
        private List<Relevance> reList = new List<Relevance>();
        private List<User> uList = new List<User>();
        private List<Role> rList = new List<Role>();
        private List<HideFiled> hList = new List<HideFiled>();

        private UserRbac rbacU;
        private RoleRbac rbacR;
        private Relevance rel = new Relevance();
        private HideFiled hide = new HideFiled();
        [TestMethod()]
        public void GetBindRelevanceTest()
        {
            Initia();
            var r0 = rList.Where(o => o.Name == "r0").First();
            //r0绑定一个u0
            var rel = rbacR.GetBindRelevance(r0, new Relevance(), uList.Where(o => o.Name == "u0").First(), PermissionType.User);
            Assert.AreEqual(rel.PermissionType.ToString(), "User");
            Assert.AreEqual(rel.EntityId, r0.Id);
            Assert.AreEqual(rel.EntityType, "Role");
        }

        [TestMethod()]
        public void GetRegisteHideFiledTest()
        {
            Initia();
            var u0 = uList.Where(o => o.Name == "u0").First();
            //U0注册name字段隐藏
            var hide = rbacU.GetRegisteHideFiled(u0, o => o.Name, new HideFiled());
            Assert.AreEqual(hide.EntityId, u0.Id);
            Assert.AreEqual(hide.HidePropertyName, "Name");
            Assert.AreEqual(hide.EntityType, "User");
        }

        [TestMethod()]
        public void GetBindHideFiledToOtherEntityTest()
        {
            Initia();
            var u0 = uList.Where(o => o.Name == "u0").First();
            //U0注册name字段隐藏
            hList.Add(rbacU.GetRegisteHideFiled(u0, o => o.Name, new HideFiled()) as HideFiled);
            rbacU.HideFiledQuery = hList.AsQueryable();

            //交U0的name字段对r0,r1隐藏
            var rs = rList.Where(o => o.Name == "r0" || o.Name == "r1").ToList();
            reList = new List<Relevance>();
            rs.ForEach(o => reList.Add(rbacU.GetBindHideFiledToOtherEntityRelevance(u0, new Relevance(), o, e => e.Name) as Relevance));
            rbacU.RelevanceQuery = reList.AsQueryable();

            var r0 = rList.Where(o => o.Name == "r0").First();
            var r1 = rList.Where(o => o.Name == "r1").First();

            //判断关联的个数,等于将两个角色同时绑定了一个用户的隐藏字段权限
            Assert.AreEqual(rbacU.RelevanceQuery.Count(), );
            Assert.IsTrue(rbacU.RelevanceQuery.First().PermisId == rbacU.RelevanceQuery.Last().PermisId );
            Assert.IsTrue(rbacU.RelevanceQuery.First().PermisId == rbacU.HideFiledQuery.First().Id);
            //判断关联的类型
            Assert.AreEqual(rbacU.RelevanceQuery.First().PermissionType, PermissionType.Propety.ToString());
            //关联的实体应该为角色,第一个要么是r0,要么是r1
            var firId = rbacU.RelevanceQuery.First().EntityId;
            var lasId = rbacU.RelevanceQuery.Last().EntityId;
            Assert.IsTrue(firId == r0.Id || lasId== r1.Id);

            //错误测试
            //将u2的隐藏字段name权绑定到r0,获取错误
            var u2 = uList.Where(o => o.Name == "u2").First();
            string errMsg = "";
            try
            {
                ], o => o.Name);
            }
            catch(Exception ex)
            { errMsg = ex.Message; }
            Assert.IsTrue(errMsg != "");
            Assert.AreEqual(errMsg, "当前实体还没有注册指定的字段隐藏权限,请先将其注册成隐藏字段权限");
        }

        [TestMethod()]
        public void GetBindRelevancesTest()
        {
            Initia();
            var r0 = rList.Where(o => o.Name == "r0").First();
            //r0绑定全部用户
            var rels = new List<Relevance>();
            foreach (var item in uList)
            {
                rels.Add(rbacR.GetBindRelevance(r0, new Relevance(), item, PermissionType.User) as Relevance);
            }

            Assert.AreEqual(rels.Count(), );
        }        

        [TestMethod()]
        public void GetPermissionsTest()
        {
            Initia();
            var u0 = uList.Where(o => o.Name == "u0").First();
            var r0 = rList.Where(o => o.Name == "r0").First();
            //r0绑定全部用户
            var rels = new List<Relevance>();
            foreach (var item in uList)
            {
                rels.Add(rbacR.GetBindRelevance(r0, new Relevance(), item, PermissionType.User) as Relevance);
            }
            rels.ForEach(o => o.Id = Guid.NewGuid());
            rbacR.RelevanceQuery = rels.AsQueryable();
            //获取r0绑定的全部权限
            var ps = rbacR.GetPermissions(r0, uList.AsQueryable());
            Assert.AreEqual(ps.Count(), );
            //过滤找出其中一条
            var p0 = rbacR.GetPermissions(r0, uList.AsQueryable(), null, o => o.Name == "u0").First();
            Assert.AreEqual(p0.Id, u0.Id);
            Assert.AreEqual(p0, u0);
        }

        [TestMethod()]
        public void GetHideFiledsTest()
        {
            Initia();
            //将所有用户的tel字段注册隐藏
            uList.ForEach(o=>hList.Add(rbacU.GetRegisteHideFiled(o,e=>e.Telephone,new HideFiled())as HideFiled));
            rbacU.HideFiledQuery = hList.AsQueryable();
            rbacR.HideFiledQuery = hList.AsQueryable();

            //将u0,u1的隐藏字段权限绑定到r0
            var r0 = rList.Where(o => o.Name == "r0").First();
            var us = uList.Where(o => o.Name == "u0" || o.Name == "u1");
            us.ToList().ForEach(o =>
            reList.Add(rbacU.GetBindHideFiledToOtherEntityRelevance(o,new Relevance(),r0,e=>e.Telephone) as Relevance));
            //设置仓储
            rbacU.RelevanceQuery = reList.AsQueryable();rbacR.RelevanceQuery = rbacU.RelevanceQuery;
            //查询r0的所有隐藏字段权限
            var query = rbacR.GetHideFileds(r0);
            Assert.AreEqual(query.Count(), );

            var first = query.First();
            var last = query.Last();
            var u0 = us.First();
            Assert.IsTrue(first.EntityId == u0.Id || last.EntityId == u0.Id);
            var ps = rbacR.GetHideFiledPermissions(r0, uList.AsQueryable()).ToList();

            Assert.IsTrue(ps[].Telephone == ].Telephone == null);
        }        

    }
}

测试

尝试封装适用于权限管理的通用API

说说我自认为这么做的好处

1、不从数据库错综复杂的关系链去考虑问题,以具体一个实体为根本,把它和它的权限关联关系,通过一个关联类来表达。那么任何一个实体既可以是权限的拥有者,本身也可是一个权限。可以很容易地为自己绑定资源,也可以把自己当成资源绑定到其它实体。

2、封装一个API可以减少代码冗余,简单服务层API。

3、有一定的可重用性,只需要继承相关的实体基类。

4、不限制权限系统的复杂程度,可伸缩性高。就算你是一个超简单的权限管理,你也可以拿来用,就算你的权限系统再复杂,你的最终目标不就是根据当前登陆用户来获取他能访问,操作的资源吗?可以的啊。你如果觉得关联太多,数据表过大,你可以根据类型分表啊。

5、 不依赖于前端,后端部分,能够给前端提供登陆用户以及用户相应的权限和资源,我觉得这已经够了,前端只需要据此做出相应的呈现,至于用什么前端,后端并不关心,做到不依赖前端

6、综上所述我觉得这不应该是基于角色的权限管理,角色只是一个比较大的实体,它能对许多拥有相似权限的用户进行分类管理而已。而应该就叫它权限管理,如果把每一个实体看成整个系统的资源,那么可以说是基于资源的权限管理系统。将角色和用户独立出来,看一个角色或一个用户拥有什么资源。

瞧瞧我在服务层整合的API

服务层中有一个抽象的基本服务类,封装了最基本的基于EF的数据CURD操作,当然具体操作是在Repository层,这里只是通过Domain层的接口调用了。我把rbac做为一个静态属性加到其中,其它的所有服务类都继承它,这样其它的服务类就都有了一个RBAC操作实例类了。至于为什么 要是静态的,我是考虑到一旦关联类发生改变,它的相应属性也应该改变。具体看代码吧

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using YZY.Domain;

namespace YZY.Service
{
    public abstract class BaseService<TEntity,TKey> where TEntity:BaseEntity<TKey>, IEntity<TKey>
    {
        protected ILogger logger = DependencyContext.Resolve<ILogger>();
        private IRepository<TEntity, TKey> repostory = DependencyContext.Resolve<IRepository<TEntity, TKey>>();
        public static RBACManage<TEntity, TKey> rbac { get; set; }= new RBACManage<TEntity, TKey>();

        public virtual void Add(TEntity entity)
        {
            repostory.Add(entity);
        }
        public virtual void Add(IEnumerable<TEntity>entities)
        {
            repostory.Add(entities);
        }
        public void Remove(TEntity entity)
        {
            repostory.Remove(entity);
        }

        public void Remove(Expression<Func<TEntity,bool>>filter)
        {
            foreach(var item in repostory.Find(filter).ToList())
            {
                Remove(item);
            }
        }
        public void Remove(TKey id)
        {
            repostory.Remove(id);
        }
        public void Update(TEntity entity)
        {
            repostory.Update(entity);
        }
        public TEntity FindById( TKey id)
        {
            return repostory.FindById(id);
        }
        public bool Exists(Expression<Func<TEntity, bool>> filter)
        {
            return repostory.Exists(filter);
        }
        public void Commit()
        {
            repostory.Save();
        }
        public IQueryable<TEntity> Find(Expression<Func<TEntity,bool>>filter=null)
        {
            return repostory.Find(filter);
        }
        public IQueryable<TEntity>FindPage(int index,int size,Expression<Func<TEntity,bool>>filter=null)
        {
            return repostory.FindPage(index, size, filter);
        }
        public IQueryable<TEntity> FindPageForOrder<TOrder>
            (int pageIndex, int pageSize, Expression<Func<TEntity, TOrder>> orderType = null,
            Expression<Func<TEntity, bool>> filter = null)
        {
            return repostory.FindPageForOrder<TOrder>(pageIndex, pageSize, orderType, filter);
        }
        public IUnitOfWork GetUnitOfWork()
        {
            return repostory.GetUnitOfWork();
        }
    }
}

基础服务类

这里面都是一些基本的CURD没有注释

有些rbac的方法其实应该在基础服务类封装的,这样可以减少代码冗余,但是我急于测试,只写了角色和用户的相关实现。

using System;
using System.Collections.Generic;
using System.Linq;
using YZY.Domain;

namespace YZY.Service
{
    public class UserService:BaseService<User,Guid>
    {
        public UserService()
        {
            rbac.RelevanceQuery = relService.Find();
            rbac.HideFiledQuery = hideService.Find();
        }
        private RelevanceService relService = new RelevanceService();
        private HideFiledService hideService = new HideFiledService();

        #region 登陆管理
        //see LoginUserResouceService
        #endregion

        #region 用户密码加密
        public override void Add(User user)
        {

            if (string.IsNullOrEmpty(user.Password))
                throw new CustomWarring("密码不能为空", "UI", logger.LogLevel);
            base.Add(UserManage<Guid>.GetCryptPwdUser(user) as User);
        }
        public override void Add(IEnumerable<User> entities)
        {
             || entities == null)
                return;
            entities.ToList().ForEach(o => Add(o));
        }
        #endregion

        private List<Guid> hideUserIds = new List<Guid>();

        #region 为当前用户绑定权限

        /// <summary>
        /// 为当前用户绑定角色
        /// </summary>
        /// <param name="user"></param>
        /// <param name="roles"></param>
        public void BindingRoles(User user,IEnumerable<Role>roles)
        {
            BindingOhters(user, roles, PermissionType.Role);
        }

        /// <summary>
        /// 为当前用户绑定能访问的用户
        /// </summary>
        /// <param name="user"></param>
        /// <param name="users"></param>
        public void BindingUsers(User user, IEnumerable<User> users)
        {
            ) return;
            List<Relevance> list = new List<Relevance>();
            //得到所有的关联
            foreach (var u in users)
            {
                list.Add(rbac.GetBindRelevance(user, new Relevance(), u, PermissionType.User) as Relevance);
            }
            ) return;
            foreach (var r in list)
            {
                //如果数据库不存在这个关联,则添加
                if (!relService.Find().Any(o => o.EntityId == user.Id && o.PermisId == r.PermisId && o.PermissionType == PermissionType.User.ToString()))
                    relService.Add(r);
            }
            relService.Save();
        }

        /// <summary>
        /// 为当前用户绑定其它可访问的资源或权限
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="user"></param>
        /// <param name="others"></param>
        /// <param name="type"></param>
        public void BindingOhters<T>(User user, IEnumerable<T> others, PermissionType type)
            where T : BaseEntity<Guid>
        {
            ) return;
            List<Relevance> list = new List<Relevance>();
            others.ToList().ForEach(o => list.Add(rbac.GetBindRelevance(user, new Relevance(), o, type) as Relevance));
            ) return;
            list.ForEach(o =>
            {
                //如果数据库中不存在关联,则添加保存
                if (!relService.Find().Any(e => e.EntityId == user.Id && e.PermisId == o.PermisId && e.PermissionType == type.ToString()))
                {
                    relService.Add(o as Relevance);
                }
            });
            relService.Save();
        }
        #endregion

        #region 按指定的权限类型获取对应的权限id集合

        /// <summary>
        /// 根据资源类型获取用户权限实体id
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public IEnumerable<Guid> GetPermissionIds(User user, PermissionType type)
        {
            return rbac.FindPermKeys(user, o => o.PermissionType == type.ToString());
        }
        #endregion

        #region 获取当前用户的权限集合

        /// <summary>
        /// 获取指定类型的权限
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="u"></param>
        /// <param name="query"></param>
        /// <param name="type"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<T> GetPermissions<T>(User u, IQueryable<T> query, PermissionType type, Func<T, bool> filter = null)
            where T : BaseEntity<Guid>
        {
            var ups = rbac.GetPermissionsForType(u, type, query, filter);
            var roles = GetRoles(u, null);
             || roles == null) return ups;
            var rService = new RoleService();
            )
            {
                foreach (var r in roles)
                {
                    ups = ups.Union(rService.GetPermissions(r, query, type, filter)).ToList();
                }
                return ups.Distinct(new DistinctEntityId<T, Guid>());
            }
            IEnumerable<T> list = new List<T>();
            foreach (var r in roles)
            {
                list = list.Union(rService.GetPermissions(r, query, type, filter)).ToList();
            }
            return list.Distinct(new DistinctEntityId<T, Guid>());
        }

        /// <summary>
        /// 获取当前用户的所有角色
        /// </summary>
        /// <param name="user"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<Role> GetRoles(User user,Func<Role,bool>filter=null)
        {
            var rService = new RoleService().Find();
            return rbac.GetPermissionsForType(user, PermissionType.Role, rService, filter);
        }

        /// <summary>
        /// 获取所有可访问的模块
        /// </summary>
        /// <param name="user"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<Module> GetModules(User user, Func<Module, bool> filter = null)
        {
            var query = new ModuleService().Find();
            return GetPermissions(user, query, PermissionType.Module, filter);
        }

        /// <summary>
        /// 获取所有可访问的操作
        /// </summary>
        /// <param name="user"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<ActionPermission> GetActions(User user, Func<ActionPermission, bool> filter = null)
        {
            var query = new ActionService().Find();
            return GetPermissions(user, query, PermissionType.Action, filter);
        }

        /// <summary>
        /// 获取所有可访问的按钮
        /// </summary>
        /// <param name="user"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<ButtonPermission> GetButtons(User user, Func<ButtonPermission, bool> filter = null)
        {
            var query = new ButtonService().Find();
            return GetPermissions(user, query, PermissionType.Button, filter);
        }

        /// <summary>
        /// 获取所有可访问的控制器
        /// </summary>
        /// <param name="user"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<ControlPermission> GetContorls(User user, Func<ControlPermission, bool> filter = null)
        {
            var query = new ContorlService().Find();
            return GetPermissions(user, query, PermissionType.Contorller, filter);
        }

        /// <summary>
        /// 获取所有可访问的组资源
        /// </summary>
        /// <param name="user"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<Group> GetGroups(User user, Func<Group, bool> filter = null)
        {
            var query = new GroupService().Find();
            return GetPermissions(user, query, PermissionType.Group, filter);
        }

        /// <summary>
        /// 获取当前用户所有能访问的用户
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public IEnumerable<User> GetUsers(User user, Func<User, bool> filter = null)
        {
            IEnumerable<User> list = new List<User>();
            //获取隐藏字段的用户
            var hideUsers = GetHideFiledUsers(user);
            ) list = list.Union(hideUsers);
            //获取可以访问的用户
            var us = rbac.GetPermissionsForType(user, PermissionType.User, Find(), filter);
            ) list = list.Union(us);

            var roles = GetRoles(user, null);
             || roles == null) return list;
            var rService = new RoleService();
            foreach(var r in roles)
            {
                list = list.Union(rService.GetUsers(r, filter));
            }
            return list.Distinct(new DistinctEntityId<User, Guid>());
        }

        /// <summary>
        /// 获取被隐藏字段的用户,被隐藏字段的用户对应的字段的值被改成null
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public IEnumerable<User> GetHideFiledUsers(User user)
        {
            return rbac.GetHideFiledPermissions(user, Find());
        }
        #endregion

        #region 隐藏字段权限
        /// <summary>
        /// 将当前用户将指定的字段对指定的角色隐藏
        /// </summary>
        /// <param name="user"></param>
        /// <param name="filedName"></param>
        /// <param name="roles"></param>
        public void HideFiledToRoles(User user, string filedName, IEnumerable<Role> roles)
        {
            //保存申请的隐藏字段权限
            var hide = rbac.GetRegisteHideFiled(user, filedName, new HideFiled()) as HideFiled;
            if (!hideService.Find().Any(o => o.EntityId == user.Id && o.HidePropertyName == filedName))
            {
                hideService.Add(hide);
                hideService.Save();
            }
            )
            {
                foreach (var r in roles)
                {
                    var rel = rbac.GetBindHideFiledToOtherEntityRelevance(user, new Relevance(), r, filedName);
                    //如数据库中不存在这个关联,则添加
                    if (!relService.Find().Any(o => o.EntityId == r.Id &&
                    o.PermissionType == PermissionType.Propety.ToString() && o.PermisId == hide.Id))
                    {
                        relService.Add(rel as Relevance);
                    }
                    //如果这些角色本身对当前用户有关联,则删除这个关联
                    if (relService.Find().Any(o => o.EntityId == r.Id && o.PermisId == user.Id &&
                    o.PermissionType == PermissionType.User.ToString()))
                    {
                        var roleU = relService.Find().Where(o => o.EntityId == r.Id && o.PermisId == user.Id &&
                        o.PermissionType == PermissionType.User.ToString()).First() as Relevance;
                        relService.Remove(roleU);
                    }
                }
                relService.Save();
            }

        }
        /// <summary>
        /// 将当前用户指定的字段对其它用户隐藏
        /// </summary>
        /// <param name="user"></param>
        /// <param name="filedName"></param>
        /// <param name="others"></param>
        public void HideFiledToUsers(User user, string filedName, IEnumerable<User> others)
        {
            //保存申请的隐藏字段权限
            var hide = rbac.GetRegisteHideFiled(user, filedName, new HideFiled()) as HideFiled;
            if (!hideService.Find().Any(o => o.EntityId == user.Id && o.HidePropertyName == filedName))
            {
                hideService.Add(hide);
                hideService.Save();
            }
            )
            {
                foreach (var r in others)
                {
                    var rel = rbac.GetBindHideFiledToOtherEntityRelevance(user, new Relevance(), r, filedName);
                    //如数据库中不存在这个关联,则添加
                    if (!relService.Find().Any(o => o.EntityId == r.Id &&
                    o.PermissionType == PermissionType.Propety.ToString() && o.PermisId == hide.Id))
                    {
                        relService.Add(rel as Relevance);
                    }
                    //如果这些用户本身对当前用户有关联,则删除这个关联
                    if (relService.Find().Any(o => o.EntityId == r.Id && o.PermisId == user.Id &&
                    o.PermissionType == PermissionType.User.ToString()))
                    {
                        var roleU = relService.Find().Where(o => o.EntityId == r.Id && o.PermisId == user.Id &&
                        o.PermissionType == PermissionType.User.ToString()).First() as Relevance;
                        relService.Remove(roleU);
                    }
                }
                relService.Save();
            }
        }
        #endregion

    }
}

用户服务类

using System;
using System.Collections.Generic;
using System.Linq;
using YZY.Domain;

namespace YZY.Service
{
    public  class RoleService:BaseService<Role,Guid>
    {
        public RoleService()
        {
            rbac.RelevanceQuery = relService.Find();
            rbac.HideFiledQuery = hideService.Find();
        }
        private RelevanceService relService = new RelevanceService();
        private HideFiledService hideService = new HideFiledService();

        #region 为角色绑定权限
        /// <summary>
        /// 将角色绑定到用户
        /// </summary>
        /// <param name="role"></param>
        /// <param name="users"></param>
        public void BindingToUsers(Role role,IEnumerable<User>users)
        {
            var uService = new UserService();
            users.ToList().ForEach(o =>
            {
                uService.BindingRoles(o, new List<Role> { role });
            });
        }
        /// <summary>
        /// 为角色绑定能访问的用户
        /// </summary>
        /// <param name="role"></param>
        /// <param name="users"></param>
        public void BindingUsers(Role role, IEnumerable<User>users)
        {
            ) return;
            List<Relevance> list = new List<Relevance>();
            //得到所有的关联
            foreach(var u in users)
            {
                list.Add(rbac.GetBindRelevance(role, new Relevance(), u, PermissionType.User) as Relevance);
            }
            ) return;
            foreach(var r in list)
            {
                //如果数据库不存在这个关联,则添加
                if (!relService.Find().Any(o => o.EntityId == role.Id && o.PermisId == r.PermisId&&o.PermissionType==PermissionType.User.ToString()))
                    relService.Add(r);
            }
            relService.Save();
        }

        /// <summary>
        /// 为角色绑定其它可访问的资源或权限
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="role"></param>
        /// <param name="others"></param>
        /// <param name="type"></param>
        public void BindingOhters<T>(Role role, IEnumerable<T> others, PermissionType type)
            where T : BaseEntity<Guid>
        {
            ) return;
            List<Relevance> list = new List<Relevance>();
            others.ToList().ForEach(o => list.Add(rbac.GetBindRelevance(role, new Relevance(), o, type) as Relevance));
            ) return;
            list.ForEach(o =>
            {
                //如果数据库中不存在关联,则添加保存
                if (!relService.Find().Any(e => e.EntityId == role.Id && e.PermisId == o.PermisId && e.PermissionType == type.ToString()))
                {
                    relService.Add(o as Relevance);
                }
            });
            relService.Save();
        }
        #endregion

        #region 按指定的权限类型获取对应的权限id集合

        /// <summary>
        /// 根据资源类型获取用户权限实体id
        /// </summary>
        /// <param name="role"></param>
        /// <returns></returns>
        public IEnumerable<Guid> GetPermissionIds(Role role, PermissionType type)
        {
            return rbac.FindPermKeys(role, o => o.PermissionType == type.ToString());
        }
        #endregion

        #region 获取角色的权限集合

        /// <summary>
        /// 获取指定类型的权限
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="role"></param>
        /// <param name="query"></param>
        /// <param name="type"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<T>GetPermissions<T>(Role role,IQueryable<T>query,PermissionType type,Func<T,bool>filter=null)
            where T:BaseEntity<Guid>
        {
            return rbac.GetPermissionsForType(role, type, query, filter);
        }

        /// <summary>
        /// 获取所有可访问的模块
        /// </summary>
        /// <param name="role"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<Module> GetModules(Role role, Func<Module, bool> filter = null)
        {
            return rbac.GetPermissions(role, new ModuleService().Find(), o => o.PermissionType == PermissionType.Module.ToString(), filter);
        }

        /// <summary>
        /// 获取所有可访问的操作
        /// </summary>
        /// <param name="role"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<ActionPermission> GetActions(Role role, Func<ActionPermission, bool> filter = null)
        {
            return rbac.GetPermissions(role, new ActionService().Find(), o => o.PermissionType == PermissionType.Action.ToString(), filter);
        }

        /// <summary>
        /// 获取所有可访问的按钮
        /// </summary>
        /// <param name="role"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<ButtonPermission> GetButtons(Role role, Func<ButtonPermission, bool> filter = null)
        {
            var query = new ButtonService().Find();
            return rbac.GetPermissions(role, query, o => o.PermissionType == PermissionType.Button.ToString(), filter);
        }

        /// <summary>
        /// 获取所有可访问的控制器
        /// </summary>
        /// <param name="role"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<ControlPermission> GetContorls(Role role, Func<ControlPermission, bool> filter = null)
        {
            var query = new ContorlService().Find();
            return rbac.GetPermissions(role, query, o => o.PermissionType == PermissionType.Contorller.ToString(), filter);
        }

        /// <summary>
        /// 获取所有可访问的组资源
        /// </summary>
        /// <param name="role"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<Group> GetGroups(Role role, Func<Group, bool> filter = null)
        {
            var query = new GroupService().Find();
            return rbac.GetPermissions(role, query, o => o.PermissionType == PermissionType.Group.ToString(), filter);
        }

        /// <summary>
        /// 获取角色所有能访问的用户
        /// </summary>
        /// <param name="role"></param>
        /// <returns></returns>
        public IEnumerable<User> GetUsers(Role role, Func<User, bool> filter = null)
        {
            var hideUsers = GetHideFiledUsers(role);
            var uService = new UserService();
            var us = rbac.GetPermissionsForType(role, PermissionType.User, uService.Find(), filter);
            return hideUsers.Union(us);
        }

        /// <summary>
        /// 获取被隐藏字段的用户,被隐藏字段的用户对应的字段的值被改成null
        /// </summary>
        /// <param name="role"></param>
        /// <returns></returns>
        public IEnumerable<User> GetHideFiledUsers(Role role)
        {
            var uService = new UserService();
            return rbac.GetHideFiledPermissions(role, uService.Find());
        }
        #endregion

    }
}

角色服务类

之所以rbac是静态属性,希望下面的几个类,能说明我的心意,当然这样有可能会影响性能了

using System.Linq;
using YZY.Domain;

namespace YZY.Service
{
    public class RBACQueryHelper
    {
        public static void RefreshQuery()
        {
            IQueryable<Relevance> relQ= new RelevanceService().Find(null);
            IQueryable<HideFiled> hideQ = new HideFiledService().Find(null);

            if (ContorlService.rbac.RelevanceQuery != relQ)
                ContorlService.rbac.RelevanceQuery = relQ;

            if (ActionService.rbac.RelevanceQuery != relQ)
                ActionService.rbac.RelevanceQuery = relQ;

            if (ButtonService.rbac.RelevanceQuery != relQ)
                ButtonService.rbac.RelevanceQuery = relQ;

            if (ModuleService.rbac.RelevanceQuery != relQ)
                ModuleService.rbac.RelevanceQuery = relQ;

            if (GroupService.rbac.RelevanceQuery != relQ)
                GroupService.rbac.RelevanceQuery = relQ;

            if (RoleService.rbac.RelevanceQuery != relQ)
                RoleService.rbac.RelevanceQuery = relQ;
            if (RoleService.rbac.HideFiledQuery != hideQ)
                RoleService.rbac.HideFiledQuery = hideQ;

            if (UserService.rbac.RelevanceQuery != relQ)
                UserService.rbac.RelevanceQuery = relQ;
            if (UserService.rbac.HideFiledQuery != hideQ)
                UserService.rbac.HideFiledQuery = hideQ;
        }
    }
}
using System;
using YZY.Domain;

namespace YZY.Service
{
    public class RelevanceService:BaseService<Relevance,Guid>
    {
        public void Save()
        {
            Commit();
            RBACQueryHelper.RefreshQuery();
        }
        //此处应该写一些删除关联的方法
    }
}

由于rbac没有依赖数据库,所以没有数据库检测,服务层在调用rbac的同时还要做数据库数据检测,比如如果要添加的关联已经存在于数据库中,我们则不用添加。用户服务类是最复杂的,因为我这里添加了用户可以隐藏字段的权限,这个权限完全是用户主动设置的,比如用户A不想让一些角色或用户看到他的电话号码,他可以对其隐藏相关字段。还有就是用户的所有权限或资源都要并集他所属性的角色的权限或资源,然后去掉重复的。对隐藏字段的用户,在获取的时候相关的字段已经被改成了null值,前端可据此做相应的处理。

在角色服务类中,有一个方法是绑定角色能访问的用户,还有一个方法就是把角色绑定到用户,即把角色做为资源绑定给用户。这两个方法有点绕。

using Microsoft.VisualStudio.TestTools.UnitTesting;
using YZY.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YZY.Domain;
using YZY.Infrastructrue;
using Autofac;
using YZY.Datas.EF;

namespace YZY.Service.Tests
{
    [TestClass()]
    public class UserServiceTests
    {
        void Initia()
        {
            DependencyContext.Dependency = new AutofacAdapter();
            AutofacAdapter.ClientInitializer(o =>
            {
                o.RegisterType<AESCrypt>().As<ICryptPwd>();
                o.RegisterType<LogHelper>().As<ILogger>();
                o.RegisterType<LocalCacheAdapter>().As<ICache>();
                o.RegisterType<YZYDbContext>().As<IUnitOfWork>();
                o.RegisterGeneric(typeof(EFBaseRepository<,>)).As(typeof(IRepository<,>));
            });
            uService = new UserService();
            rService = new RoleService();
            bService = new ButtonService();
            relService = new RelevanceService();
            hideService = new HideFiledService();

        }
        private UserService uService;
        private RoleService rService;
        private ButtonService bService;
        private RelevanceService relService;
        private HideFiledService hideService;
        [TestMethod()]
        public void BindingRolesTest()
        {
            Initia();
            var r0r1 = rService.Find(o => o.Name == "role-0" || o.Name == "role-1");
            var u0 = uService.Find(o => o.Name == "u-0").First();
            //为u0绑定两个角色r0r1
            uService.BindingRoles(u0, r0r1.ToList());
            Assert.AreEqual(UserService.rbac.RelevanceQuery.Count(), );
            Assert.AreEqual(relService.Find().Count(), );
        }

        [TestMethod()]
        public void BindingUsersTest()
        {
            Initia();
            //将u0绑定对u9,19的访问权限
            var u0 = uService.Find(o => o.Name == "u-0").First();
            var others = uService.Find(o => o.Name == "u-9" || o.Name == "u-19").ToList();
            uService.BindingUsers(u0, others);
            Assert.AreEqual(relService.Find(o => o.PermissionType == );
            Assert.AreEqual(relService.Find(o => o.PermissionType == "User").First().EntityId ,u0.Id);
        }

        [TestMethod()]
        public void BindingOhtersTest()
        {
            //将u0绑定所有的按钮
            Initia();
            //for(int i=0;i<20;i++)
            //{
            //    bService.Add(new ButtonPermission { Name = "btn" + i });

            //}
            //bService.Commit();
            var u0 = uService.Find(o => o.Name == "u-0").First();
            var btns = bService.Find().ToList();
            uService.BindingOhters(u0, btns, PermissionType.Button);
            Assert.AreEqual(relService.Find(o => o.PermissionType == );
            Assert.AreEqual(relService.Find(o => o.PermissionType == "Button").First().EntityId, u0.Id);
        }

        [TestMethod()]
        public void GetButtonsTest()
        {
            Initia();
            var u0 = uService.Find(o => o.Name == "u-0").First();
            //u0绑定了r0,r1;20个btn;u9,u19;
            var btns = uService.GetButtons(u0);
            Assert.AreEqual(btns.Count(), );
            //将r0绑定btn0权限
            var r0 = rService.Find(o => o.Name == "role-0").First();
            var btn0 = bService.Find(o => o.Name == "btn0").First();
            rService.BindingOhters(r0, new List<ButtonPermission> { btn0 }, PermissionType.Button);
            Assert.AreEqual(uService.GetButtons(u0).Count(), );
        }

        [TestMethod()]
        public void GetContorlsTest()
        {
            Initia();
            //将r2绑定3个btn
            var r2 = rService.Find(o => o.Name == "role-2").First();
            var btns = bService.Find(o => o.Name == "btn2" || o.Name == "btn12" || o.Name == "btn10").ToList();
            rService.BindingOhters(r2, btns, PermissionType.Button);
            //u2绑定r2
            var u2 = uService.Find(o => o.Name == "u-2").First();
            uService.BindingRoles(u2, new List<Role> { r2 });
            var query = uService.GetButtons(u2);
            Assert.AreEqual(query.Count(), );
        }

        [TestMethod()]
        public void GetUsersTest()
        {
            Assert.Fail();
        }

        [TestMethod()]
        public void GetHideFiledUsersTest()
        {
            Initia();
            //目前情况是u0本身有对u9,u19的访问权限,但u9对其隐藏了name字段
            var u0 = uService.Find(o => o.Name == "u-0").First();
            var filed = uService.GetHideFiledUsers(u0);
            var all = uService.GetUsers(u0);
            Assert.AreEqual(filed.Count(), );
            Assert.AreEqual(all.Count(), );
            var name = all.Select(o => o.Name).ToList();
            Assert.IsTrue(name[] == ] == "u-19");
        }

        [TestMethod()]
        public void HideFiledToRolesTest()
        {
            Assert.Fail();
        }

        [TestMethod()]
        public void HideFiledToUsersTest()
        {
            Initia();
            var u0 = uService.Find(o => o.Name == "u-0").ToList();
            var u9 = uService.Find(o => o.Name == "u-9").First();
            uService.HideFiledToUsers(u9, "Name", u0);
            Assert.AreEqual(hideService.Find().Count(), );
            var u = u0.First();
            var us = relService.Find(o => o.EntityId == u.Id && o.PermissionType == "User");
            Assert.AreEqual(us.Count(), );
            var get = uService.GetUsers(u).ToList();
        }
    }
}

测试代码

这个测试之前数据库中已经有20个用户和5个角色

static void InsertData()
        {
            ; i < ; i++)
            {
                User u = new User { Name = "u-" + i, Account = "ac-" + i, Email = "em-" + i, Telephone = "tel-" + i ,Password="pwd"+i};
                uService.Add(u);
            }
            ; i < ; i++)
            {
                Role r = new Role { Name = "role-" + i, Type = "type-" + i };
                rService.Add(r);
            }
            uService.Commit();
            rService.Commit();
        }

想想我的难处

我是一个自学者,一个业外人士,目前经济情况相当糟糕,现在又基本处于失业状态了。公司效益相当差,面临关闭。我年纪不小,学历不高,我迫切的希望能进入这个行业,但是我怕一轮一轮的简历筛选,工作经验要求,面试笔试,加之曾经有个有点权威的人对我说过,我根本找不到相关工作,像我这样年纪的基本都是项目经理级别起步的了,我年纪太大,学习能力不如刚毕业的实习生,如果是他他不会招我。尽管如此,我却依然想尝试,我不怕年纪大重头来,我愿意以实习生的身份求得一个学习实习机会,丢掉的面子已经够多了,我相信我会比一般人更努力。希望路过的不介意我的经验和学历,有能力收留我这类人的看官,能给我提供一个入行的机会。如果您觉得没能力直接收留我,觉得我写的还行,右边有个打赏块,可以尝试打赏几个找工作的公交费以示鼓励。如果您对我的文章有点兴趣,无论好坏有什么想法请留言,我会积极参与。目前我是做到哪写到哪,并没有一个前端实现,如果有兴趣要源码的,我只能提供半成品。

谈谈,看看,试试,说说,瞧瞧,想想

上一篇:MProtect使用小计【三】 – 权限管理


下一篇:ASP.NET 开发必备知识点(2):那些年追过的ASP.NET权限管理