.Net MVC实现角色-API权限验证的一种方式

阅文时长 | 1.15分钟 字数统计 | 1844.8字符
主要内容 | 1、引言&背景 2、部分设计分享 3、声明与参考资料
『.Net MVC实现角色-API权限验证的一种方式』
编写人 | SCscHero 编写时间 | 2022/3/27 PM9:31
文章类型 | 系列 完成度 | 已完成
座右铭 每一个伟大的事业,都有一个微不足道的开始。

一、引言&背景   完成度:100%

a) 应对问题&背景

RBAC的权限设计已经应用到越来越多的系统中,然而在一些老项目中,对各个Role可访问的API并未做相关的限制,从而引发高级别的安全漏洞。这里介绍一种简单且比较可靠的设计。

b) 应用场景

  • 在RBAC的权限设计中,对应的Role进行API权限检验。

c) 分析思路

  1. 首先对"Role-API"的对应关系进行梳理,一般用一张关系表进行存储。存储在缓存中。(缓存写入节点、过期时间按需而论),比如:博主是在项目启动的Global.asax文件中写入缓存,设置为永不过期,理由是项目功能已基本不迭代(当然这也有很多不便之处:例如如果按博主的这种方式,如果寄托于IIS,需要Stop后Start来更新缓存)。
  2. 新增一个特性AjaxAuthorizeAttribute继承于身份验证AuthorizeAttribute特性,重写OnAuthorization方法;写入逻辑为:用服务器端存储的当前用户的上下文信息中的Role取其可访问的API集合,再与访问接口的URL做判断,判断此Role是否具备该接口的权限,若不具备权限则返回403.cshtml的静态页;此特性用来标记需验证的控制器或Action。
  3. 以上这一种方式即可简单且相对可靠的实现对RBAC权限设计中,Role-API的权限校验。

二、部分设计分享   完成度:100%

a) Role-API存储关系表

以下是存储"Role-API"对应关系的关系表,如图博主是用了五个字段来存:步长为10主键ID、RoleID、RoleName、Url、APiDesc。其中RoleName和ApiDesc是冗余字段,方便开发者阅读设计的,在缓存中存的对象不需要存RoleName和ApiDesc。

其中有一点需要注意的是URL,一般都是维护成/Area/Controller/Action。

b) AjaxAuthorizeAttribute

AjaxAuthorizeAttribute是自定义的重写身份验证特性的类。以下为一个通用设计。其中需要注意的是第2步骤,在取AuthorizationContext中的请求路由时,有几种取数方式,注意区别。在以下代码示例中有一些解释可以参考。

    public class AjaxAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            //1. 从缓存中取数据的Obj对象,并将Obj转换为实体对象。(转换方式是否最优有待思考优化,先不做讨论)
            IList objList = (IList)CacheDataLogic.CacheHelper.GetData(ParameterNames.CacheRoleResourceAll);
            var res = objList.Cast<MMC_RoleResourceMap>();
            //ApplicationContext是我们封装的取用户上下文的类
            var RoleName = ((RoleModel)CacheDataLogic.CacheHelper.GetData(ParameterNames.CacheClientUserRole + ApplicationContext.Current.LoginUserInfo.UserModel.UserID)).RoleName;
            //2. 取当前用户API集合是否包含请求集合。
            //filterContext.HttpContext.Request.RawUrl//路由后带参数,取到的数据是带参数的。比如:/SCscCon/SCscAction?scsc=6666。
            //filterContext.HttpContext.Request.Path//可以过滤掉?后的参数,比如:/Claim/SCscAction /?scsc=4430。但无法解决不带?的参数URL。比如:	/Claim/ClaimApply/4430
            var spA = filterContext.HttpContext.Request.Path.Split('/');//如果上一种不是想用来判断的参数,可以做截取。比如下面这样。
            //3. 判断当前用户API集合是否包含请求集合。
            if (res.Where(o => o.RoleName == RoleName && o.Url == ("/" + spA[1] + "/" + spA[2])).Count() > 0)
            {
                base.OnAuthorization(filterContext);
            }
            else
            {
                HttpContext.Current.Response.Redirect("~/AuthorizeError.html");
                return;
            }
        }
    }

另外建议大家可以了解一下AuthorizeAttribute对象,其中的各方法的调用顺序可以研究研究,比如HandleUnauthorizedRequest方法等等,都是需要掌握的知识点。

c) 非授权通用页

用来重定向的通用页html代码,记录一下,后面方便复用。

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
	<meta charset="utf-8" />
</head>
<body>
    <hgroup>
        <h1>Error.</h1>
        <h2>An error occurred while processing your request.</h2>
    </hgroup>
</body>
</html>

三、声明与参考资料   完成度:100%

原创博文,未经许可请勿转载。

如有帮助,欢迎点赞、收藏、关注。如有问题,请评论留言!如需与博主联系的,直接博客私信SCscHero即可。

posted on 2022-04-04 00:37  SCscHero  阅读(689)  评论(0编辑  收藏  举报

导航