目录

目标

介绍

使用的组件

建立新项目

迁移

角色,创建角色

分配角色权限

分配角色给用户

访问限制

结论

有趣的阅读


具有动态角色和动态权限的MVC应用程序具有更改系统中不同功能的授权的能力。

  • 下载源代码976.8 KB

目标

通常对于小型组织,没有预定义的固定角色/用户。他们在成长和繁荣的过程中学习过程。在这种情况下,我们通常会获得创建角色和动态分配权限的要求,而又不损害安全性,因为大多数提出要求的人还不确定角色或策略。因此,在这里,我们将尝试学习基于角色的动态授权。

介绍

在本文中,我们将尝试学习如何创建动态角色以及如何为这些角色动态分配权限。这是前一篇文章“数据库的MVC 6动态导航菜单”的续篇。

以前,我们学习了如何从数据库动态生成菜单。现在,根据该菜单,我们需要验证用户角色的权限。我们将学习:

  • 创建一个新角色
  • 动态分配/删除角色权限
  • 向用户分配/删除新角色

使用的组件

这是构建和测试所提供的演示代码所需的组件。

  • 下载最新的Visual Studio 2019 16.4.5社区版,专业版或企业版
  • 我正在使用SQL Server Developer版本17.9.1,可以从此链接下载

我们将使用带有C#和MVC项目模板的.NET Core Framework 3.1版,因此让我们开始吧。

在上一篇文章中,我添加了一些额外的字段,例如ExternalUrl &,DisplayOrder 以提供一个选项,可以在菜单中添加外部链接并根据用户的选择设置菜单项的顺序。

建立新项目

打开Visual Studio 2019,然后单击创建新项目以开始新项目。

它将在下面的屏幕中显示更多选择,因此选择C所有平台Web,然后选择ASP.NET Core Web Application并单击Next

在这里,我们需要提供项目名称,然后单击Create

选择.NET CoreASP.NET Core 3.1Model-View-Controller作为模板,选择个人用户帐户作为身份验证,然后单击Create,Visual Studio将为您创建一个具有所有这些设置的新项目。

设置项目后,让我们根据模型创建数据库,确保在appsettings.json文件中设置连接字符串。我将使用本地主机作为Windows身份验证的服务器,以下是我的连接字符串。

"DefaultConnection": "Server=localhost;Database=DynamicPermissions;
Trusted_Connection=True;MultipleActiveResultSets=true"

我已创建NavigationMenu来存储菜单名称和RoleMenuPermission实体来存储角色权限。

[Table(name: "AspNetNavigationMenu")]
public class NavigationMenu
{[Key][DatabaseGenerated(DatabaseGeneratedOption.Identity)]public Guid Id { get; set; }public string Name { get; set; }[ForeignKey("ParentNavigationMenu")]public Guid? ParentMenuId { get; set; }public virtual NavigationMenu ParentNavigationMenu { get; set; }public string Area { get; set; }public string ControllerName { get; set; }public string ActionName { get; set; }public bool IsExternal { get; set; }public string ExternalUrl { get; set; }public int DisplayOrder { get; set; }[NotMapped]public bool Permitted { get; set; }public bool Visible { get; set; }
}[Table(name: "AspNetRoleMenuPermission")]
public class RoleMenuPermission
{public string RoleId { get; set; }public Guid NavigationMenuId { get; set; }public NavigationMenu NavigationMenu { get; set; }
}

这里是我的数据库上下文,我们重写OnModelCreating来定义RoleId及NavigationMenuId作为键,因为我们并不需要为这个表标识键。

public class ApplicationDbContext : IdentityDbContext
{public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options): base(options){}public DbSet<RoleMenuPermission> RoleMenuPermission { get; set; }public DbSet<NavigationMenu> NavigationMenu { get; set; }protected override void OnModelCreating(ModelBuilder builder){builder.Entity<RoleMenuPermission>().HasKey(c => new { c.RoleId, c.NavigationMenuId});base.OnModelCreating(builder);}
}

迁移

现在我们需要运行迁移,然后更新数据库,Enable-Migrations命令已过时,因此我们需要从Migrations文件夹中删除所有内容,然后运行add migration命令。

add-migration InitialVersion

这是我的数据库表,如下所示:

如果想了解更详细的信息,可阅读另一篇文章:

  • 播种数据MVC 6 .NET Core应用程序

新版本的EF .NET Core在OnModelCreating函数中的ModelBuilder 对象上有HasData,但现在,在此演示中,我们将继续使用上述方法。

在DbInitializer中修改,添加了新的权限并分配给了管理员角色,我们需要这些权限在数据库中可用,以便稍后可以分配和验证用户角色。

new NavigationMenu()
{Id = new Guid("F704BDFD-D3EA-4A6F-9463-DA47ED3657AB"),Name = "External Google Link",ControllerName = "",ActionName = "",IsExternal = true,ExternalUrl = "https://www.google.com/",ParentMenuId = new Guid("13e2f21a-4283-4ff8-bb7a-096e7b89e0f0"),DisplayOrder=2,Visible = true,
},
new NavigationMenu()
{Id = new Guid("913BF559-DB46-4072-BD01-F73F3C92E5D5"),Name = "Create Role",ControllerName = "Admin",ActionName = "CreateRole",ParentMenuId = new Guid("13e2f21a-4283-4ff8-bb7a-096e7b89e0f0"),DisplayOrder=3,Visible = true,
},
new NavigationMenu()
{Id = new Guid("3C1702C5-C34F-4468-B807-3A1D5545F734"),Name = "Edit User",ControllerName = "Admin",ActionName = "EditUser",ParentMenuId = new Guid("13e2f21a-4283-4ff8-bb7a-096e7b89e0f0"),DisplayOrder=3,Visible = false,
},
new NavigationMenu()
{Id = new Guid("94C22F11-6DD2-4B9C-95F7-9DD4EA1002E6"),Name = "Edit Role Permission",ControllerName = "Admin",ActionName = "EditRolePermission",ParentMenuId = new Guid("13e2f21a-4283-4ff8-bb7a-096e7b89e0f0"),DisplayOrder=3,Visible = false,
},

在先前的实现中,我在数据服务中添加了两个新功能。

我们将从NavigationMenu连接中获取所有已定义的权限,并将其分配给具有Permitted = true的角色,因此基于此,我们可以将复选框呈现为选中/未选中。

public async Task<List<NavigationMenuViewModel>> GetPermissionsByRoleIdAsync(string id)
{var items = await (from m in _context.NavigationMenujoin rm in _context.RoleMenuPermissionon new { X1 = m.Id, X2 = id } equals new { X1 = rm.NavigationMenuId, X2 = rm.RoleId }into rmpfrom rm in rmp.DefaultIfEmpty()select new NavigationMenuViewModel(){Id = m.Id,Name = m.Name,Area = m.Area,ActionName = m.ActionName,ControllerName = m.ControllerName,IsExternal = m.IsExternal,ExternalUrl = m.ExternalUrl,DisplayOrder = m.DisplayOrder,ParentMenuId = m.ParentMenuId,Visible = m.Visible,Permitted = rm.RoleId == id}).AsNoTracking().ToListAsync();return items;
}//Remove old permissions for that role id and assign changed permissionspublic async Task<bool> SetPermissionsByRoleIdAsync(string id, IEnumerable<Guid> permissionIds)
{var existing = await _context.RoleMenuPermission.Where(x => x.RoleId == id).ToListAsync();_context.RemoveRange(existing);foreach (var item in permissionIds){await _context.RoleMenuPermission.AddAsync(new RoleMenuPermission(){RoleId = id,NavigationMenuId = item,});}var result = await _context.SaveChangesAsync();return result > 0;
}

这是我的Admin控制器,有关操作的详细实现,我们可以在zip中查看代码。简单的实现,没有魔术代码:)。我们只需要将[Authorize("Authorization")]放在任何要告诉应用程序验证授权的操作上,或者如果所有操作都受到保护,则可以在控制器级别使用它。

[Authorize("Authorization")]
public class AdminController : Controller
{private readonly UserManager<IdentityUser> _userManager;private readonly RoleManager<IdentityRole> _roleManager;private readonly IDataAccessService _dataAccessService;private readonly ILogger<AdminController> _logger;public AdminController(UserManager<IdentityUser> userManager,RoleManager<IdentityRole> roleManager,IDataAccessService dataAccessService,ILogger<AdminController> logger){_userManager = userManager;_roleManager = roleManager;_dataAccessService = dataAccessService;_logger = logger;}public async Task<IActionResult> Roles() {}[HttpPost]public async Task<IActionResult> CreateRole(RoleViewModel viewModel) {}public async Task<IActionResult> Users() {}public async Task<IActionResult> EditUser(string id){}[HttpPost]public async Task<IActionResult> EditUser(UserViewModel viewModel){}        public async Task<IActionResult> EditRolePermission(string id){}[HttpPost]public async Task<IActionResult> EditRolePermission(string id, List<NavigationMenuViewModel> viewModel){}
}

这是我们呈现复选框列表的方式。

<form asp-action="EditRolePermission"><div class="form-group"><ul style="list-style-type: none;">@for (var i = 0; i < Model.Count; i++){<li><input type="checkbox" asp-for="@Model[i].Permitted" /><label style="margin-left:10px;" asp-for="@Model[i].Permitted">@Model[i].Name</label><input type="hidden" asp-for="@Model[i].Id" /><input type="hidden" asp-for="@Model[i].Name" /></li>}</ul></div><div class="form-group"><input type="submit" value="Save" class="btn btn-primary" /><a asp-action="Roles">Back to List</a></div>
</form>

现在,我们可以通过以下方式使用“Admin User”来运行和测试系统:

  • 用户名:admin@test.com
  • 密码:P @ ssw0rd

角色,创建角色

以下是在迁移过程中创建的角色列表:

在“创建角色屏幕中,可以在系统中添加新角色。

分配角色权限

在“角色列表”中,如果单击“编辑权限按钮,它将带我们进入“权限”屏幕,其中列出了已选中分配的权限的所有权限。

现在,我们可以更改这些权限并进行保存,以使其对该角色下的用户有效。因此,让我们尝试对其进行更改。

我们将取消选中“外部Google链接和创建角色”。

现在,当我保存这些更改,然后再保存该角色的“编辑”权限。

如您所见,现在这两个权限都未选中,并且菜单中也不存在。

现在,我可以尝试通过粘贴URL 来访问“创建角色页面,因此它应该根据我的更新权限对我进行验证,并向我抛出“访问被拒绝”。

如果我们复制具有访问权限的某个页面的URL,然后与其他无法访问该页面的用户登录并粘贴复制的URL,则可以验证相同的错误。

分配角色给用户

我们可以使用“编辑按钮查看用户列表。

通过编辑,我们将能够向用户分配/删除角色,单击“编辑按钮后,我们可以看到以下屏幕,其中包含所有角色的列表作为系统中可用的复选框列表。

因此,现在我们有了基于这些界面的屏幕,用于创建新角色,角色列表,编辑用户,角色的编辑权限,我们需要验证授权。

访问限制

我们将为此目的使用“授权处理程序”,但是在现实世界的系统中,可以更改角色并将其重新分配给不同的用户,或者在一个特定的时间段内,一个用户可以拥有多个角色,而不是在开发时已经定义许多策略或角色。请牢记这一点,我们将给予最终用户自由以授予对其定义的角色的权限,以便具有这些角色的客户/员工可以根据其角色和权限执行其职责。

我们将对AuthorizationHandler进行泛化,使其在具有数据库权限的情况下动态工作。我们需要创建一个授权需求并从IAuthorizationRequirement接口继承。现在,我们可以创建AuthorizationHandler泛型并使用泛型传递我们的要求,然后可以重写该HandleRequirementAsync函数。从端点获取Controller和Action并从数据库中检查权限。通过这种方法,授权将与MVC耦合,但这没关系,因为已针对该特定目的和用途编写了该处理程序。

public class AuthorizationRequirement : IAuthorizationRequirement { }public class PermissionHandler : AuthorizationHandler<AuthorizationRequirement>
{private readonly IDataAccessService _dataAccessService;public PermissionHandler(IDataAccessService dataAccessService){_dataAccessService = dataAccessService;}protected async override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizationRequirement requirement){if (context.Resource is RouteEndpoint endpoint){endpoint.RoutePattern.RequiredValues.TryGetValue("controller", out var _controller);endpoint.RoutePattern.RequiredValues.TryGetValue("action", out var _action);endpoint.RoutePattern.RequiredValues.TryGetValue("page", out var _page);endpoint.RoutePattern.RequiredValues.TryGetValue("area", out var _area);var isAuthenticated = context.User.Identity.IsAuthenticated;if (isAuthenticated && _controller != null && _action != null &&await _dataAccessService.GetMenuItemsAsync(context.User, _controller.ToString(), _action.ToString())){context.Succeed(requirement);}}}
}

为了演示和限制讨论范围,我们将从数据库中即时检查/验证它,以提高性能,我们可以使用Cache来保留权限,以减少对每个资源访问进行授权检查的数据库调用。可以将角色权限添加到缓存中的用户声明和权限,以提高性能。

结论

我们已经通过迁移创建了数据库,并在开发环境下启动了我们的项目。登录的用户可以根据动态定义的角色权限查看菜单项和页面。源代码已附加。我鼓励您下载示例代码,然后运行并查看。

有趣的阅读

在开发人员社区和.NET Core安全团队之间,仍在进行一些讨论。

  • Add First Class Citizen Support of ORing Policies #1356
  • Revisit PolicyProvider (large # permissions) scenarios/support #917
  • Authorization policies without using magic strings
  • Authorization policies without using magic strings
  • Future AuthZ improvements list #4670

基于MVC .NET Core动态角色的授权相关推荐

  1. ASP.NET Core 2.1中基于角色的授权

    授权是来描述用户能够做什么的过程.例如,只允许管理员用户可以在电脑上进行软件的安装以及卸载.而非管理员用户只能使用软件而不能进行软件的安装以及卸载.它是独立的而又与验证配合使用,需要身份验证机制.对于 ...

  2. ASP.NET Core 使用 JWT 自定义角色/策略授权需要实现的接口

    目录 ① 存储角色/用户所能访问的 API ② 实现 IAuthorizationRequirement 接口 ③ 实现 TokenValidationParameters ④ 生成 Token ⑤ ...

  3. Asp.net中基于Forms验证的角色验证授权[转]

    Asp.net的身份验证有有三种,分别是"Windows | Forms | Passport",其中又以Forms验证用的最多,也最灵活. Forms 验证方式对基于用户的验证授 ...

  4. 【转载】Asp.Net中基于Forms验证的角色验证授权

    Asp.Net的身份验证有有三种,分别是"Windows | Forms | Passport",其中又以Forms验证用的最多,也最灵活. Forms 验证方式对基于用户的验证授 ...

  5. Asp.net中基于Forms验证的角色验证授权

    Asp.net的身份验证有有三种,分别是"Windows | Forms | Passport",其中又以Forms验证用的最多,也最灵活. Forms 验证方式对基于用户的验证授 ...

  6. 创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表

    创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表 创建数据模型类(POCO类) 在Models文件夹下添 ...

  7. SpringSecurity - 用户动态授权 及 动态角色权限

    一.SpringSecurity 动态授权 上篇文章我们介绍了SpringSecurity的动态认证,上篇文章就说了SpringSecurity 的两大主要功能就是认证和授权,既然认证以及学习了,那本 ...

  8. ABP 详解系列9:基于ABP框架实现RBAC(角色访问控制)

    基于ABP框架实现RBAC(角色访问控制) 本文详细讲解了基于ABP框架实现RBAC(角色访问控制)的方法,文中通过示例代码介绍的非常详细.对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考 ...

  9. SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例(转)...

    SpringBoot整合mybatis.shiro.redis实现基于数据库的细粒度动态权限管理系统实例 shiro 目录(?)[+] 前言 表结构 maven配置 配置Druid 配置mybatis ...

  10. 基于ASP.Net Core开发的一套通用后台框架

    基于ASP.Net Core开发一套通用后台框架 写在前面 这是本人在学习的过程中搭建学习的框架,如果对你有所帮助那再好不过.如果您有发现错误,请告知我,我会第一时间修改. 知其然,知其所以然,并非重 ...

最新文章

  1. 基于数据挖掘的旅游推荐APP(一):开篇
  2. 现在做Android开发有前途吗?复习指南
  3. 人生有五不:不等、不管、不看、不做、不给
  4. 天池 在线编程 木材加工(二分查找)
  5. mysql给指定数据增加前后缀update,替换replace字段值
  6. (30)Verilog HDL系统函数:$stop
  7. ASP.NET CORE的Code Fist后Models更改了怎么办?
  8. 如何在Windows 10上安装Python
  9. 三大抽样分布——卡方分布、t分布、F分布
  10. 计算机二级access无忧考吧破解,无忧考吧access模拟考试软件
  11. zblog博客模板-zblog插件免费-支持PHP以及ASP
  12. OA系统审批流程思路梳理
  13. Eric6中使用PYQT5在窗口显示图片
  14. 重新理了下思路一个人的孤独
  15. 微x模块怎么导入主题_模块
  16. C/C++小程序学习:n*n魔方矩阵实现每行、每列、每一对角线上的元素之和相等
  17. android监控电话录音,Android例子源码实现电话录音监听的安卓例子
  18. BlackBerry 9850 应用:大众点评,美食家必备哦
  19. 安装EA后win10提示系统资源不足,无法完成请求服务的解决方法
  20. 干货:Google Adsense最全注册技巧及收款方式

热门文章

  1. uni-app 官网教程
  2. SQL如何删除重复数据
  3. 离散数学左孝凌版答案
  4. Windows 系统常用工具软件
  5. 多智能体强化学习—QMIX
  6. php去除前两位,php去除前后空格的实现方法
  7. 单片机c语言入门自学,单片机C语言入门教程
  8. java除法运算符号怎么打_Java实用教程:运算符
  9. Linux下挂载U盘、ISO、光盘、rpm
  10. “OneNMP”-超高性价比的实用网管工具