Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# ASP.NETMVC5:为基于web的产品设计基于角色的访问系统的最佳方法_C#_Asp.net Mvc - Fatal编程技术网

C# ASP.NETMVC5:为基于web的产品设计基于角色的访问系统的最佳方法

C# ASP.NETMVC5:为基于web的产品设计基于角色的访问系统的最佳方法,c#,asp.net-mvc,C#,Asp.net Mvc,我可能不得不开发一个ERP系统,它不是面向公众的网站,而是由特定组织的不同用户使用。我正在寻找一个最好的想法,以设计用户权限系统领域的管理员分配访问权的角色,并分配给用户的角色。角色明智的用户可以或不能访问网页 最初我有一些想法,我想简单地讨论一下。告诉我我的方向对吗 假设我的网站将有人力资源相关页面、销售和账户相关页面。因此,任何用户都不能访问HR或帐户、采购相关页面。管理员授予用户访问该区域的权限的方式 假设在第一页中,管理员将在主表中保存所有控制器名称及其操作名称 对于管理员页面,我将显示

我可能不得不开发一个ERP系统,它不是面向公众的网站,而是由特定组织的不同用户使用。我正在寻找一个最好的想法,以设计用户权限系统领域的管理员分配访问权的角色,并分配给用户的角色。角色明智的用户可以或不能访问网页

最初我有一些想法,我想简单地讨论一下。告诉我我的方向对吗

假设我的网站将有人力资源相关页面、销售和账户相关页面。因此,任何用户都不能访问HR或帐户、采购相关页面。管理员授予用户访问该区域的权限的方式

假设在第一页中,管理员将在主表中保存所有控制器名称及其操作名称

对于管理员页面,我将显示所有控制器和操作名称以及隐藏的id

比如说人力资源总监

因此,管理员首先创建几个角色,如人力资源、帐户、采购和销售等

管理员首先从下拉列表中选择角色

下面我将显示控制器操作名称。管理员只需勾选控制器名称和几个动作名称,并保存在数据库中的信息。我将在其中保存角色id和控制器id、操作id

通过这种方式,我可以将控制器和操作id附加到角色。当分配此角色时,将使用角色id和用户id保存在数据库中

在控制器级别或操作级别,我将编写一个自定义属性,通过该属性我将验证特定用户是否具有访问此操作的角色和权限

因为我不想用这种方式硬编码角色

[Authorize(Roles = "Producer")]
[Authorize(Roles = "Admin")]
public ActionResult Details(int id) {
    // Only available to users who are Producers AND Editors
}
我更愿意按照下面的方式来做

public class DynamicRoleAuthorizeAttribute: AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var controller = httpContext.Request.RequestContext
            .RouteData.GetRequiredString("controller");
        var action = httpContext.Request.RequestContext
            .RouteData.GetRequiredString("action");
        // feed the roles here
        Roles = string.Join("," ,_rolesProvider.Get(controller, action));
        return base.AuthorizeCore(httpContext);
    }
}

[DynamicRoleAuthorize]
public ActionResult MyAction()
{

}
但我的问题是,如果任何开发人员错误地更改了cs文件中的任何操作或控制器名称,那么我如何管理整个过程。错误地,如果任何控制器名称或操作名称发生更改,那么我的想法将无法正常工作

因此,请指导我如何在asp.net mvc 5中设计这种权限系统,它将不依赖于控制器名称或操作名称。如果这些名称发生更改,那么权限也应该起作用

请用idea或一些代码示例指导我在基于web的产品中实现。或者告诉我其他有经验的开发人员,他们是如何处理这种情况的。寻找实施这种许可制度的最佳指南

我搜索了谷歌,但没有找到我可以遵循的好的解决方案。有很多关于asp.NETMVC的文章,但这是任何产品中非常重要的一部分,根据我对谷歌搜索的了解,它并没有涉及太多内容

如果有人获得与此主题相关的任何文章链接,请与我共享RBAC系统的url。若这篇文章是关于asp.net核心mvc的,那个么这也将帮助我了解这个想法


谢谢

我的第一个倾向是投票结束这个问题,因为它正在寻求意见;几乎是一篇描述如何设计灵活许可系统的博客文章。网上有很多,包括我链接到的微软文档。所以,如果你在谷歌上找不到任何东西,那么你正在努力寻找你想要的东西

基本上,权限系统涉及参与者(通常是用户)和安全资源(例如API)。挑战在于能够为用户分配权限,并在用户调用API时验证这些权限。API所需的权限可以且应该硬编码:

[FuncPermission("GetHrData")]
public ActionResult Details(int id) {
    // Only available to users that have GetHrData Permission
}
然后定义角色(用户组),并为角色分配权限。在我看来,您不应该硬编码
Authorize
属性中的角色

你将从这里走向何方

  • 在数据库中保留您的权限。因此,在开发新功能时,您添加了新的权限,但您的管理模块没有改变
  • 开发用户和组(角色)的常规管理功能。优选地,向组分配权限;不适用于用户
  • 开发将用户权限与访问特定API所需的权限相匹配的授权中间件<代码>FuncPermissionAttribute类是此类中间件的一部分
  • 如果可行(也就是说,没有那么多权限),您可以将它们存储为Microsoft Identity中的角色声明,并将其作为标头的一部分传递。请注意,标头有大小限制(在大多数web服务器上为8-16K);因此,您可能需要从数据库中获取这些声明
  • 一个用户可能属于多个具有潜在重叠权限的组;因此,您需要“展平”权限才能进行匹配

  • 坦白说,还有很多其他的事情你也需要考虑。开发ERP相当复杂(即使您不打算与市场上众多优秀的ERP系统竞争)。如果你是第一次实施授权,我建议雇佣一个能帮助你并培训你的人。可能不便宜-但肯定比最终得到需要废弃和重写的系统便宜

    我回顾了一个旧的MVC 5项目,发现这是一个起点

    我假设每个控制器都有读/写/编辑/删除或基本CRUD操作。我创建了一些代码,使用反射来查看项目,对于每个控制器,生成了四个声明-控制器名称和四个可能操作中的每一个。我手动创建了菜单选项的声明。这样,视图可以隐藏或禁用不允许用户访问的菜单选项

    这是一个典型的控制器动作。在我的例子中,一个用户想要取消一张发票,这将是成功还是失败——所有的工作都在一个业务模型中执行,以保持控制器操作的轻量级。无论视图是否授予用户取消发票的权限,执行操作的安全带和支架都是有效的
    [FuncPermission("GetHrData")]
    public ActionResult Details(int id) {
        // Only available to users that have GetHrData Permission
    }
    
    [ClaimsAuthorize("InvoicesController", "Edit")]
    [ValidateAntiForgeryToken]
    [HttpPost]
    public async Task<ActionResult> CancelInvoice(InvoiceView v)
    {
        var user = _userManager.FindById(User.Identity.GetUserId());
    
        await _bs.CancelInvoice_Async(v, ModelState, user.Id);
    
        if (ModelState.IsValid)
        {
            // state saved
            return RedirectToAction("Edit", new { id = v.InvoiceId });
        }
    
        // not saved
        return View("Edit", v);
    }
    
    
    
    
        /// <summary>
        /// Allows use of an authorisation attribute on controllers and controller methods
        /// </summary>
        public class ClaimsAuthorizeAttribute : AuthorizeAttribute
        {
            private string claimType;
            private string claimValue;
        
            /// <summary>
            /// Authorise using a claim by type (and optional value)
            /// </summary>
            /// <param name="type">The Claim Type - Usually [Controller]_[Action]</param>
            /// <param name="value">The Claim Value, usually one of Read | Edit | Create | Delete, or some other relevant value</param>
            public ClaimsAuthorizeAttribute(string type, string value = "")
            {
                this.ClaimType = type;
                this.ClaimValue = value;
            }
        
            /// <summary>
            /// Gets the Claim Type - Usually [Controller]_[Action]
            /// </summary>
            public string ClaimType { get => claimType; protected set => claimType = value; }
        
            /// <summary>
            /// Gets the Claim Value, usually one of Read | Edit | Create | Delete, or some other relevant value
            /// </summary>
            public string ClaimValue { get => claimValue; protected set => claimValue = value; }
        
            public override void OnAuthorization(AuthorizationContext filterContext)
            {
                // assume not authorised
                bool isAuthorised = false;
        
                // check user exists
                if (filterContext.HttpContext.User != null)
                {
                    // get user by claim principle
                    var user = filterContext.HttpContext.User as System.Security.Claims.ClaimsPrincipal;
        
                    if (user != null && user.HasClaim(ClaimType, ClaimValue))
                    {
                        // user has a claim of the correct type
                        isAuthorised = true;
                    }
                }
        
                if (isAuthorised)
                {
                    filterContext.Result = null;
                    base.OnAuthorization(filterContext);
                }
                else
                {
                    // we don't use 401 as this will cause a login loop :  base.HandleUnauthorizedRequest(filterContext);
                    // Forbidden message will be shown
                    filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden, "You are forbidden to access this resource");
                }
            }
        }