使用.NET从零实现基于用户角色的访问权限控制

使用.NET从零实现基于用户角色的访问权限控制

本文将介绍如何实现一个基于.NET RBAC 权限管理系统,如果您不想了解原理,可查看推送的另一篇文章关于Sang.AspNetCore.RoleBasedAuthorization[1] 库是使用介绍,直接使用该库即可。

背景

在设计系统时,我们必然要考虑系统使用的用户,不同的用户拥有不同的权限。主流的权限管理系统都是RBAC模型(Role-Based Access Control 基于角色的访问控制)的变形和运用,只是根据不同的业务和设计方案,呈现不同的显示效果。

在微软文档中我们了解了《基于角色的授权》[2],但是这种方式在代码设计之初,就设计好了系统角色有什么,每个角色都可以访问哪些资源。针对简单的或者说变动不大的系统来说这些完全是够用的,但是失去了灵活性。因为我们不能自由的创建新的角色,为其重新指定一个新的权限范围,毕竟就算为用户赋予多个角色,也会出现重叠或者多余的部分。

RBAC(Role-Based Access Control)即:基于角色的权限控制。通过角色关联用户,角色关联权限的方式间接赋予用户权限。

ece103324217a92d16189bddefd4fe33.png

RBAC模型可以分为:RBAC0、RBAC1、RBAC2、RBAC3 四种。其中RBAC0是基础,也是最简单的,今天我们就先从基础的开始。

资源描述的管理

在开始权限验证设计之前我们需要先对系统可访问的资源进行标识和管理。在后面的权限分配时,我们通过标识好的资源进行资源和操作权限的分配。

资源描述

创建一个 ResourceAttribute 继承 AuthorizeAttribute 和 IAuthorizationRequirement 资源描述属性,描述访问的角色需要的资源要求。通过转化为 Policy 来对 策略的授权[3] 提出要求。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class ResourceAttribute: AuthorizeAttribute, IAuthorizationRequirement
{private string _resouceName;private string? _action;/// <summary>/// 设置资源类型/// </summary>/// <param name="name">资源名称</param>/// <exception cref="ArgumentNullException">资源名称不能为空</exception>public ResourceAttribute(string name){if (string.IsNullOrEmpty(name)){throw new ArgumentNullException(nameof(name));}string[] resourceValues = name.Split('-');_resouceName = resourceValues[0];if (resourceValues.Length > 1){Action = resourceValues[1];}else{Policy = resourceValues[0];}}/// <summary>/// 获取资源名称/// </summary>/// <returns></returns>public string GetResource(){return _resouceName;}/// <summary>/// 获取操作名称/// </summary>public string? Action{get{return _action;}set{_action = value;if (!string.IsNullOrEmpty(value)){//把资源名称跟操作名称组装成PolicyPolicy = _resouceName + "-" + value;}}}
}

获得所有资源

我们标识好系统中的资源后,还需要获取到我们最终程序中都标识有哪些资源,这里就需使用 ASP.NET Core 中的应用程序模型[4]。可以在程序启动时获取到所有的 Controller 和 Controller 中的每一个方法,然后通过查询 ResourceAttribute 将其统一存储到静态类中。

创建一个 ResourceInfoModelProvider 继承 IApplicationModelProvider,其执行顺序我们设置为=> -989。其执行顺序:

•首先 (Order=-1000):DefaultApplicationModelProvider•然后(Order= -990):AuthorizationApplicationModelProvider CorsApplicationModelProvider•接着是这个 ResourceInfoModelProvider

其核心代码如下:

/// <summary>
/// 基于其 Order 属性以倒序调用
/// </summary>
/// <param name="context"></param>
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{if (context == null){throw new ArgumentNullException(nameof(context));}//获取所有的控制器List<ResourceAttribute> attributeData = new List<ResourceAttribute>();foreach (var controllerModel in context.Result.Controllers){//得到ResourceAttribute//Controller 的特性var resourceData = controllerModel.Attributes.OfType<ResourceAttribute>().ToArray();if (resourceData.Length > 0){attributeData.AddRange(resourceData);}//Controller 中的每个方法的特性foreach (var actionModel in controllerModel.Actions){var actionResourceData = actionModel.Attributes.OfType<ResourceAttribute>().ToArray();if (actionResourceData.Length > 0){attributeData.AddRange(actionResourceData);}}}// 整理信息集中存入全局foreach (var item in attributeData){ResourceData.AddResource(item.GetResource(), item.Action);}
}

授权控制的实现

接下来我们要对授权控制来进行编码实现,包含自定义授权策略的实现和自定义授权处理程序。

动态添加自定义授权策略

关于自定义授权策略提供程序[5]的说明,这里不再赘述微软的文档,里面已经介绍了很详细,这里我们通过其特性可以动态的创建自定义授权策略,在访问资源时我们获取到刚刚标识的 Policy 没有处理策略,就直接新建一个,并传递这个策略的权限检查信息,当然这只是一方面,更多妙用,阅读文档里面其适用范围的说明即可。

/// <summary>
/// 自定义授权策略
/// 自动增加 Policy 授权策略
/// </summary>
/// <param name="policyName">授权名称</param>
/// <returns></returns>
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{// 检查这个授权策略有没有AuthorizationPolicy? policy = _options.GetPolicy(policyName);if (policy is null){_options.AddPolicy(policyName, builder =>{builder.AddRequirements(new ResourceAttribute(policyName));});}return Task.FromResult(_options.GetPolicy(policyName));
}

授权处理程序

前面我们已经可以动态创建授权的策略,那么关于授权策略的处理[6]我们可以实现 AuthorizationHandler 根据传递的策略处理要求对本次请求进行权限的分析。

internal class ResourceAuthorizationHandler : AuthorizationHandler<ResourceAttribute>
{/// <summary>/// 授权处理/// </summary>/// <param name="context">请求上下文</param>/// <param name="requirement">资源验证要求</param>/// <returns></returns>protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ResourceAttribute requirement){// 需要有用户if (context.User is null) return Task.CompletedTask;if (context.User.IsInRole(ResourceRole.Administrator) // 超级管理员权限,拥有 SangRBAC_Administrator 角色不检查权限|| CheckClaims(context.User.Claims, requirement) // 符合 Resource 或 Resource-Action 组合的 Permission){context.Succeed(requirement);}return Task.CompletedTask;}/// <summary>/// 检查 Claims 是否符合要求/// </summary>/// <param name="claims">待检查的claims</param>/// <param name="requirement">检查的依据</param>/// <returns></returns>private bool CheckClaims(IEnumerable<Claim> claims, ResourceAttribute requirement){return claims.Any(c =>string.Equals(c.Type, ResourceClaimTypes.Permission, StringComparison.OrdinalIgnoreCase)&& (string.Equals(c.Value, requirement.GetResource(), StringComparison.Ordinal)|| string.Equals(c.Value, $"{requirement.GetResource()}-{requirement.Action}", StringComparison.Ordinal)));}
}

这里我们提供了一个内置固定角色名的超级管理员用户,其请求不进行权限检查。

最后

这里我们已经实现了简单的 RBAC 权限设计,之后我们主要在生成 JWT 时带上可访问资源的Permission即可。

new Claim(ResourceClaimTypes.Permission,"查询")

当然,如果直接放在 jwt 中会让 Token 变得很长,虽然我其实并不理解微软的 ClaimTypes 使用一个URI标识,如果有了解的朋友可以帮我解个惑,万分感谢 https://stackoverflow.com/questions/72293184/ 。

回到这个问题,我们可以再设计一个中间件,在获取到用户的角色名时将其关于角色权限的ClaimTypes加入到 content.User 即可。关于这一方面的详细介绍和实现可以看下一篇文章。

本文介绍的相关代码已经提供 Nuget 包,并开源了代码,感兴趣的同学可以查阅:https://github.com/sangyuxiaowu/Sang.AspNetCore.RoleBasedAuthorization

如有错漏之处,敬请指正。

References

[1] Sang.AspNetCore.RoleBasedAuthorization: https://www.nuget.org/packages/Sang.AspNetCore.RoleBasedAuthorization
[2] 《基于角色的授权》: https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/roles?view=aspnetcore-6.0
[3] 策略的授权: https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/policies?view=aspnetcore-6.0
[4] 使用 ASP.NET Core 中的应用程序模型: https://learn.microsoft.com/zh-cn/aspnet/core/mvc/controllers/application-model?view=aspnetcore-6.0
[5] 自定义授权策略提供程序: https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-6.0
[6] 授权策略的处理: https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/policies?view=aspnetcore-6.0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/281661.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据归一化

数据归一化 数据的标准化是将数据按比例缩放&#xff0c;使之落入一个小的特定区间&#xff0c;一般为0到1之间。在某些比较和评价的指标处理中经常会用到&#xff0c;去除数据的单位限制&#xff0c;将其转化为无量纲的纯数值&#xff0c;便于不同单位或量级的指标能够进行比较…

.NET性能系列文章一:.NET7的性能改进

这些方法在.NET7 中变得更快照片来自 CHUTTERSNAP[1] 的 Unsplash[2]欢迎阅读.NET 性能系列的第一章。这一系列的特点是对.NET 世界中许多不同的主题进行研究、比较性能。正如标题所说的那样&#xff0c;本章节在于.NET7 中的性能改进。你将看到哪种方法是实现特定功能最快的方…

丁洪波 -- 不要“ 总是拿着微不足道的成就来骗自己”

都市快报实盘大赛25期&#xff1a;于海飞/丁洪波荣获冠亚军 七禾网 时间&#xff1a;2010-11-02 12:47:05 来源&#xff1a;期货中国10月30日下午&#xff0c;2010年浙商期货实盘大赛第三季度&#xff08;都市快报实盘大赛第25期&#xff09;颁奖典礼在天科大厦浙商期货大会议室…

面试专题(Mysql及Mongodb)

2019独角兽企业重金招聘Python工程师标准>>> mysql面试题 1. 各个数据库存储引擎区别 mysql的存储引擎是针对表进行设置的&#xff0c;一个库的不同表可以设置不同的存储引擎&#xff0c;mysql默认支持多种存储引擎&#xff0c;以适用不同领域的数据库应用需要&…

织梦网站翻页php,dedecms织梦网站列表页和内容页分页样式

织梦分页标签{dede:pagelist istitem"index,pre,next,end,option,info," listsize"5"/}&#xff0c;{dede:prenext getpre/}&#xff0c;{dede:prenext getnext/}。默认样式和使用模板css样式布局不一样,这时又不想重写样式&#xff0c;我们可以修改织梦标…

KDD走进阿里 数百专家聚集探讨产学研一体化

6月29日&#xff0c;由阿里巴巴集团、中国中文信息学会、KDD China联合主办的数据挖掘前沿发展与未来论坛在杭州举行&#xff0c;会议吸引了来自国际顶级高校和知名企业的近300名专家学者到场参会、近30000人在线观看。论坛除了分享最新的数据挖掘领域最新科研成果及研发思路外…

php模板引擎循环start,PHP模板引擎Smarty内建函数section,sectionelse用法详解

本文实例讲述了PHP模板引擎Smarty内建函数section,sectionelse用法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;section 是 Smarty 模板中除了 foreach 以外的另一种处理循环的方案&#xff0c;section 比 foreach 要灵活&#xff0c;就像是一个改进的 foreach 语句…

OpenHarmony操作系统与龙芯2K1000LA芯片完成适配,龙架构平台获得开源鸿蒙认证

近日&#xff0c;龙芯中科与软通动力控股公司鸿湖万联共同完成OpenHarmony操作系统与龙芯2K1000LA处理器的适配&#xff0c;“乘风1000”开发板&#xff08;搭载龙芯2K1000LA&#xff09;荣获OpenHarmony生态产品兼容性证书。至此&#xff0c;万物互联的OpenHarmony生态体系再次…

struts2开发action 的三种方法以及通配符、路径匹配原则、常量

struts2开发action 的三种方法 1、继承ActionSupport public class UserAction extends ActionSupport {// Action中业务处理方法public String login() {System.out.println("UserAction.login()"); // return "success";return SUCCESS;} } 2、实现…

left join 和 inner join

2019独角兽企业重金招聘Python工程师标准>>> left join 和 inner join 首先 MySQL 中 inner join 的效率确实要高于 left join。所以没必要使用 left join 转弯成 inner join 的效果。这样不但效率降低&#xff0c;可读性也会降低。 Number1 select from t1 left j…

Vue3+.NET6,轻松开发管理后台!(可复用)

在GitHub是没找到简单好用的Vue3.NET6管理后台项目&#xff0c;有收藏的请评论区分享。这里分享一套Vue3 Axios TS Vite Element Plus .NET 6 WebAPI JWT SqlSugar的通用管理后台&#xff0c;前后端分离架构&#xff0c;各种最新框架组件&#xff0c;实现了管理后台几乎…

iOS网络请求安全认证(JWT,RSA)

在网络世界中&#xff0c;安全是一个很重要的问题&#xff0c;以往的HTTP请求已经不能承担这个安全任务&#xff0c;抓包工具一抓&#xff0c;你的所有网络请求全都曝光。当然&#xff0c;你可能会采用加密算法来加密数据&#xff0c;但是这仍然不够。 在移动端和服务器的通信过…

linux下mariadb大小写敏感

2019独角兽企业重金招聘Python工程师标准>>> Linux下安装好mariadb后&#xff0c;在使用时会发现mariadb对大小写敏感&#xff0c;这对开发带来一定的不利&#xff0c;这时只要在配置文件中配置一下&#xff0c;取消大小写敏感即可&#xff1a; sudo vi /etc/MySQL/…

评论列表显示及排序,个人中心显示

1.显示所有评论{% for foo in ques.comments %} 2.所有评论排序uquestion db.relationship(Question, backrefdb.backref(comments, order_bycreat_time.desc)) 3.显示评论条数{{ ques.comments|length }} 1题代码如下&#xff1a; <h3>评论区:({{ ques.comments|length…

他俩都曾是技术大牛,创业这些年来有怎样的苦与乐?

这是头哥侃码的第263篇原创国庆假期回来&#xff0c;「头哥唠 B 唠」的直播仍在继续。这次我邀请了我工作上的老板和朋友&#xff0c;一起聊了聊关于 “技术创业路上的苦与乐”。熟悉他们两位的都知道&#xff0c;可以说是技术出身&#xff0c;然后创业当老板的代表。大家都知道…

maven,gradle本地缓存位置

gradle: 配置系统环境变量GRADLE_USER_HOME即可&#xff0c;值为缓存位置。 maven: 修改settings文件&#xff1a;maven的home路径下的conf文件夹下的settings.xml 对于有些IDEA&#xff0c;还需要配置。但是不要再打开项目后的FILE-->settings配置&#xff0c;而是需要在选…

客户端嵌套 Web 页面如何选择

客户端嵌套 Web 页面如何选择客户端嵌套 Web 页面如何选择作者&#xff1a;驚鏵在使用客户端嵌套WEB页面有一下几种方案&#xff1a;WebView2[1]Electron[2]NW.js[3]sciter[4]miniblink[5]现在国内众多桌面程序都是用了以下五种&#xff0c;因为它跨平台更为方便&#x1f447;。…

【Tomcat】Tomcat配置与优化(内存、并发、管理)【自己配置】

一、JVM内存配置优化 主要通过以下的几个jvm参数来设置堆内存的&#xff1a; -Xmx512m 最大总堆内存&#xff0c;一般设置为物理内存的1/4 -Xms512m 初始总堆内存&#xff0c;一般将它设置的和最大堆内存一样大&#xff0c;这样就不需要根据当前堆使用情况而调整堆的大…

【2018-11-15】中证1000指数的估值详情

中证1000指数选取中证500和沪深300指数样本股以外的&#xff0c;流动性好的1000只股票组成&#xff0c;与沪深300 和中证500 等形成互补。 中证1000的个股大多数是由市场上流通市值排名在 800 到 1800 名之间的个股组成&#xff0c;是一个适用范围较广的小盘指数。 中证1000的…

印度光伏巨头Adani与华为签署500MW采购合同

日前&#xff0c;印度光伏巨头Adani与华为签订了采购合同。Adani未来一年的项目全部采用华为FusionSolar3.0智能光伏解决方案&#xff0c;首期500MW采购合同已经签署&#xff0c;将采购最新的智能光伏控制器&#xff08;组串逆变器&#xff09;SUN2000-43KTL、数据采集器SmartL…