首页 文章

使用Identity Claims控制MVC菜单,控制器授权和视图

提问于
浏览
0

I've been looking for ways to implement authorisation in MVC (after years using Active Directory for everything!) using the latest Identity Claims functionality. I've posted a suggestion below but my question is, does anyone have any better ideas? Are there more "standard" ways of achieving this?

在当天,Forms Authentication Users和Roles是控制Web应用程序中的身份验证和授权的方式 . 通常使用与业务角色相对应的少量角色(例如,管理员,经理, Worker ) . 有可能(现在仍然)以更高的粒度级别使用角色,比如为每个MVC [控制器] [动作]创建一个使用字符串的角色 . 这确实需要额外的表来以较低的粒度级别管理业务角色,但它可以非常简单地完成 .

随着最新ASP.NET身份中“声明”的出现,我认为可以实现更好的授权实现 . 有很多方法可以做到这一点,我建议在下面给出一个相当小的方法 .

1 回答

  • 0

    创建一个Controller基类并重写OnActionExecuting方法 . 我为数据库中的用户保存了一些声明,其中包含控制器名称的声明类型和“读取”,“编辑”,“创建”,“删除”的声明值,这是用户对控制器 . 我已将这些声明添加到视图包中以供在视图中使用 .

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                // get user claims
                var user = filterContext.HttpContext.User as System.Security.Claims.ClaimsPrincipal;
    
                if (user != null)
                {
                    // Get all user claims on this controller. In this controler base class, [this] still gets the descendant instance type, hence name
                    List<Claim> claims = user.Claims.Where(c => c.Type == this.GetType().Name).ToList();
    
                    // set Viewbag with default authorisations on this controller
                    ViewBag.ClaimRead = claims.Any(c => c.Value == "Read");
                    ViewBag.ClaimEdit = claims.Any(c => c.Value == "Edit");
                    ViewBag.ClaimCreate = claims.Any(c => c.Value == "Create");
                    ViewBag.ClaimDelete = claims.Any(c => c.Value == "Delete");
                }
    
                base.OnActionExecuting(filterContext);
            }
    

    控制器操作使用自定义授权来保护它们

    [ClaimsAuthorize("ApplianceTypesController", "Create")]
    public ActionResult Create()
    {
        return View();
    }
    

    在Razor视图中,仅在授权时添加控件

    <p>
        @if (ViewBag.ClaimCreate)
        {
            @Html.ActionLink("Create New", "Create")
        }
    </p>
    

    我没有将额外的授权放入控制器和其他地方,而是将它放入自定义授权类中

    public class ClaimsAuthorizeAttribute : AuthorizeAttribute
    {
        private string claimType;
        private string claimValue;
    
        public ClaimsAuthorizeAttribute(string type, string value = "")
        {
            this.ClaimType = type;
            this.ClaimValue = value;
        }
    
        public string ClaimType { get => claimType; protected set => claimType = value; }
    
        public string ClaimValue { get => claimValue; protected set => claimValue = value; }
    
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.User != null)
            {
                var user = filterContext.HttpContext.User as System.Security.Claims.ClaimsPrincipal;
    
                if (user != null && user.HasClaim(ClaimType, ClaimValue))
                {
                    filterContext.Result = null;
                    base.OnAuthorization(filterContext);
                }
                else
                {
                    // we don't use 401 as this will cause a login loop :  base.HandleUnauthorizedRequest(filterContext);
                    filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden, "You are forbidden to access this resource");
                }
            }
        }
    }
    

    我没有使用标准的Site-Map,而是有一个基于Claim Types的菜单项表(忽略任何值) . 我假设对控制器及其页面的任何声明都会显示菜单项以实现目标 . 这部分视图作为菜单的一部分

    @foreach (var item in Model.Where(m => m.ParentId == null).OrderBy(m => m.SequenceNumber))
    {
        @Html.ActionLink(item.DisplayText, item.ActionName, item.ControllerName, null, new { @class = "nav-item nav-link active" })
    }
    

相关问题