天天看点

asp.net Core3.1自定义权限体系-菜单和操作按钮权限

我们在做项目项目,经常会碰到权限体系,权限体系属于系统架构的一个最底层的功能,也是非常重要的功能,几乎在每个项目都会用到。那么我们该如何设计一个比较合理的且扩展性较强的权限体系呢?

经过多天的摸索,参考多个系统以及自己的经验,《沐雪微店系统 NetCore3.1》的权限体系是这样的。

  • 一、首先确定几个重要实体的关系:用户,角色,权限;这三者之间的关系如下:
asp.net Core3.1自定义权限体系-菜单和操作按钮权限

其中:

1、用户与角色是1对多关系(  1个用户只有1个角色,1个角色可以对应多个用户);

2、角色与权限组是1对1关系( 1个角色只有1个权限组,1个权限组只有1个角色)。

3、一个权限组里包含1个菜单和多个操作按钮;

4、操作按钮预先定义好最多的情况的枚举值;(比如  查看,新增,修改,删除,审核,下载,确认,回复)

这样的架构,相对来说比较合理,适合绝大多数系统使用了。(设计的越灵活,控制起来越困难,越困难越容易出现错误,所以要根据实际的情况控制灵活到什么程度即可;不要一口吃成胖子,想要一次性搞出无限灵活的权限系统。)

  • 二、对几个实体进行CRUD单实体操作(增,删,改,查)

 1、菜单的增删改查;

asp.net Core3.1自定义权限体系-菜单和操作按钮权限

2、权限组的增删改查:

asp.net Core3.1自定义权限体系-菜单和操作按钮权限

3、用户的增删改查:

asp.net Core3.1自定义权限体系-菜单和操作按钮权限
  • 三、用代码来实现权限系统

 大家在菜单管理页面,应该注意到有3个字段:--编码,链接地址和权限值;这些是我们写代码的时候需要用的。

1、创建一个控制器的父类--BaseController,这里只要有一个可以获取当前登录者的方法即可,类似如下代码:

asp.net Core3.1自定义权限体系-菜单和操作按钮权限
asp.net Core3.1自定义权限体系-菜单和操作按钮权限
/// <summary>
        /// 当前登录者
        /// </summary>
        public PTLoginResp CurrentUser
        {
            get
            {
                PTLoginResp currentUser = new PTLoginResp();
                if (User.Identity.IsAuthenticated)
                {
                    var claimIdentity = (ClaimsIdentity)User.Identity;
                    string key = claimIdentity.FindFirst("tokenid").Value;
                    currentUser.id = LoginCredentials.PFDecodeRedisKeyOfUserId(key);

                    currentUser.user_name = claimIdentity.FindFirst("user_name").Value;
                    currentUser.real_name = claimIdentity.FindFirst("real_name").Value;
                    currentUser.mobile_phone = claimIdentity.FindFirst("mobile_phone").Value;
                    currentUser.role_id = ConvertHelper.LongParse(claimIdentity.FindFirst("role_id").Value, 0);

                }
                return currentUser;
            }
        }      

View Code

2、创建具体的业务控制器和相应的Action,并且集成BaseController;

3、创建一个Action的权限属性特性方法,比如下面代码:

/// <summary>
    /// Action的权限属性
    /// </summary>
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
    public class ActionAuthDescriptorAttribute : System.Attribute
    {

        public ActionAuthDescriptorAttribute(string NavCode, AuthTypeEnum AuthType)
        {
            this.NavCode = NavCode;
            this.AuthType = AuthType;

        }
        /// <summary>
        /// 权限组code
        /// </summary>
        public string NavCode { get; set; }

        /// <summary>
        /// 权限操作的枚举值,比如Show
        /// </summary>
        public AuthTypeEnum AuthType { get; set; }

    }      

这样,我们就可以在Action上加上这个特性了:

/// <summary>
        /// 《沐雪微店系统 Netcore 3.1》添加管理员页面
        /// </summary>
        /// <returns></returns>
        [ActionAuthDescriptor("manager_mgr", AuthTypeEnum.Add)]
        public async Task< ActionResult> Create()
        {
            ManagerInfo managerAdd = new ManagerInfo();
            managerAdd.using_type = UsingTypeEnum.sys.ToString();
            managerAdd.is_lock = false;

            List<muxue_role> roleList =await  _roleService.GetRoleList(UsingTypeEnum.sys);
            roleList = roleList.FindAll(p => p.is_sys == false);
            ViewBag.roleList = roleList;

            return View(managerAdd);
        }      

4、开始写权限验证过滤器了PrivilegeFilter:

验证的大概逻辑如下:

/// <summary>
        /// 《沐雪微店系统 Netcore 3.1》权限验证逻辑
        /// (集成BaseController的控制器,需要登录后才可以)
        /// 1、看下Controller是否有AllRightsAttribute;
        /// 2、看下Action是否有AllRightsAttribute,是否有ActionAuthDescriptorAttribute
        /// 3、(1)若Action有AllRightsAttribute,则说明任何登录者都可以访问;
        ///    (2)若Action没有AllRightsAttribute,但是Controller上有,则说明任何登录者都可以访问;
        ///    (3)若Action和Controller都没有,并且Aciton也没有ActionAuthDescriptorAttribute, 则任何人都不可以访问;
        /// 4、看下Action是否有ActionAuthDescriptorAttribute,则任何人都不可以访问;
        /// 5、若有ActionAuthDescriptorAttribute,则进行判断该Action是否有该角色的权限;
        /// </summary>
        /// <param name="context"></param>      

重点的代码如下:

public override void OnActionExecuting(ActionExecutingContext context)
        {

            var controller = context.Controller as BaseController;
            if (controller == null)
            {
                base.OnActionExecuting(context);
                return;
            }

            if (controller.CurrentUser == null)
            {
                //《沐雪微店系统 Netcore 3.1》去登录
                // context.HttpContext.ChallengeAsync().Wait();
                base.OnActionExecuting(context);
                return;
            }

            string requestMethod = context.HttpContext.Request.Method.ToLower();


            ControllerActionDescriptor ad = context.ActionDescriptor as ControllerActionDescriptor;
            bool isControllerAllRights = ad.ControllerTypeInfo.IsDefined(typeof(AllRightsAttribute), false);
            bool isActionAllRights = ad.MethodInfo.IsDefined(typeof(AllRightsAttribute), false);
            bool isActionAuthDescriptor = ad.MethodInfo.IsDefined(typeof(ActionAuthDescriptorAttribute), false);
            if (!isControllerAllRights && !isActionAllRights && !isActionAuthDescriptor)
            {
                //没有权限访问
                if (requestMethod == "get")
                {
                    context.Result = new RedirectResult("/Error/Index?msg=没有权限");
                    base.OnActionExecuting(context);
                    return;
                }
                else
                {
                    context.Result = new JsonResult(NoPrivPostJsonResult());
                    base.OnActionExecuting(context);
                    return;
                }
            }
            if (isActionAllRights)
            {
                base.OnActionExecuting(context);
                return;
            }
            if (isControllerAllRights && !isActionAllRights && !isActionAuthDescriptor)
            {
                base.OnActionExecuting(context);
                return;
            }

            if (isActionAuthDescriptor)
            {
                var authorizeAttr = ad.MethodInfo.GetCustomAttributes(typeof(ActionAuthDescriptorAttribute), false).FirstOrDefault() as ActionAuthDescriptorAttribute;
                string navCode = authorizeAttr.NavCode;
                AuthTypeEnum authType = authorizeAttr.AuthType;

                long current_role_id = controller.CurrentUser.role_id;
                bool hasRolePriv = _roleprivilegeService.HasRolePriv(current_role_id, navCode, authType).Result;
                if (!hasRolePriv)
                {//没有权限
                    if (requestMethod == "get")
                    {
                        context.Result = new RedirectResult("/Error/Index?msg=没有权限");
                        base.OnActionExecuting(context);
                        return;
                    }
                    else
                    {
                        context.Result = new JsonResult(NoPrivPostJsonResult());
                        base.OnActionExecuting(context);
                        return;
                    }
                }
            }

            base.OnActionExecuting(context);

        }      

将这个Filter添加到StartUp里:

{
                if (!env.IsDevelopment())
                {
                }
                options.Filters.Add<LogstashFilter>();
                options.Filters.Add<PrivilegeFilter>();//权限验证
                options.Filters.Add<XcActionFilter>();
                options.Filters.Add<GlobalExceptions>();
            })      

这样就完成了权限控制了。

 作者:沐雪