【问题标题】:ASP.NET MVC 4 Custom Role Authorization show/hide Edit/Delete links in ViewsASP.NET MVC 4 自定义角色授权在视图中显示/隐藏编辑/删除链接
【发布时间】:2014-11-04 12:59:08
【问题描述】:

我想根据用户的授权显示/隐藏编辑/删除链接(包括菜单项)。我已经实现了 AuthorizeAttribute 并为角色检查重写的 AuthorizeCore 提供了自定义逻辑。在检查用户是否有权查看 LinkExtensions 方法中的编辑/删除链接时,我想使用该逻辑。 这是我的设置:

public class AuthorizeActivity : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
    }

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
    {
        bool isAuthorized = base.AuthorizeCore(httpContext);
        string actionType = httpContext.Request.HttpMethod;

        string controller = httpContext.Request.RequestContext.RouteData.Values["controller"].ToString();
        string action = httpContext.Request.RequestContext.RouteData.Values["action"].ToString();

        //ADMINS
        if (controller == "Admin")
        {
            if (httpContext.User.IsInRole(Constants.Admin))
                return true;
        }
        else
        {
            //DATA READERS ONLY
            if ((action == "Details") || (action == "Index"))
            {
                if (httpContext.User.IsInRole(Constants.DataReader))
                    return true;
            }
            //DATA WRITERS & IT
            else
            {
              ...
            }
        }
        return false;
    }

我还使用了 Vivien Chevallier 的逻辑来创建此处概述的授权操作链接扩展:http://vivien-chevallier.com/Articles/create-an-authorized-action-link-extension-for-aspnet-mvc-3 现在在我看来我可以使用:

<li>@Html.ActionLinkAuthorized("Admin", "Index", "Admin",false) </li>

链接是否显示取决于用户的权限。 在我的控制器中,动作装饰有:

    [AuthorizeActivity]
    public ActionResult Index()
    {
        return View(view);
    }

除非我在我认为多余的属性中指定“角色”,否则授权链接将不起作用,如下所示:

[AuthorizeActivity(Roles = Constants.roleSalesContractAdmin)]
public ActionResult Index()
{
    return View(view);
}

我似乎无法找到重用 AuthorizeAttribute 中的逻辑的方法。理想情况下,它会像 Vivien 一样在 ActionLinkAuthorized 中调用:

public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool showActionLinkAsDisabled)
    {
        if (htmlHelper.ActionAuthorized(actionName, controllerName)) //The call to verify here -- or inside ActionAuthorized
        {
            return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes);
        }
        else
        {
            if (showActionLinkAsDisabled)
            {
                TagBuilder tagBuilder = new TagBuilder("span");
                tagBuilder.InnerHtml = linkText;
                return MvcHtmlString.Create(tagBuilder.ToString());
            }
            else
            {
                return MvcHtmlString.Empty;
            }
        }
    }

这是 ActionAuthorized 方法。 OnAuthorization 调用不会转到自定义的

public static bool ActionAuthorized(this HtmlHelper htmlHelper, string actionName, string controllerName)
    {
        ControllerBase controllerBase = string.IsNullOrEmpty(controllerName) ? htmlHelper.ViewContext.Controller : htmlHelper.GetControllerByName(controllerName);
        ControllerContext controllerContext = new ControllerContext(htmlHelper.ViewContext.RequestContext, controllerBase);
        ControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(controllerContext.Controller.GetType());
        ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);

        if (actionDescriptor == null)
            return false;
        FilterInfo filters = new FilterInfo(FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor));

        AuthorizationContext authorizationContext = new AuthorizationContext(controllerContext, actionDescriptor);
        foreach (IAuthorizationFilter authorizationFilter in filters.AuthorizationFilters)
        {
            authorizationFilter.OnAuthorization(authorizationContext); //This call
            if (authorizationContext.Result != null)
                return false;
        }
        return true;
    }

【问题讨论】:

  • 你应该看看我的anwsear,能给你一个想法:stackoverflow.com/questions/18874081/…
  • 为什么不将自定义 Authorization 属性的 AuthorizeCore 中的逻辑提取到静态方法中,放入某个静态类并在属性和帮助器中重用它?

标签: c# asp.net asp.net-mvc asp.net-mvc-4 authorization


【解决方案1】:

我遇到了类似的问题 我是这样解决的:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
    public class MyAuthorizedAttribute : AuthorizeAttribute
    {
        public bool CheckPermissions(HttpContextBase httpContext, string controller, string action)
        {
            bool authorized;

            //Validate User permissions of the way you think is best

            return authorized;
        }

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            var action = filterContext.ActionDescriptor.ActionName;
            var controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
            if (filterContext == null)
            {
                throw new ArgumentNullException(nameof(filterContext));
            }

            if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
            {
                // If a child action cache block is active, we need to fail immediately, even if authorization
                // would have succeeded. The reason is that there's no way to hook a callback to rerun
                // authorization before the fragment is served from the cache, so we can't guarantee that this
                // filter will be re-run on subsequent requests.
                throw new InvalidOperationException("AuthorizeAttribute Cannot Use Within Child Action Cache");
            }

            var skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof (AllowAnonymousAttribute), true)
                                    ||
                                    filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(
                                        typeof (AllowAnonymousAttribute), true);

            if (skipAuthorization)
            {
                return;
            }

            if (AuthorizeCore(filterContext.HttpContext) && CheckPermissions(filterContext.HttpContext, controller, action))
            {
                // ** IMPORTANT **
                // Since we're performing authorization at the action level, the authorization code runs
                // after the output caching module. In the worst case this could allow an authorized user
                // to cause the page to be cached, then an unauthorized user would later be served the
                // cached page. We work around this by telling proxies not to cache the sensitive page,
                // then we hook our custom authorization code into the caching mechanism so that we have
                // the final say on whether a page should be served from the cache.

                var cachePolicy = filterContext.HttpContext.Response.Cache;
                cachePolicy.SetProxyMaxAge(new TimeSpan(0));
                cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
            }
            else
            {
                HandleUnauthorizedRequest(filterContext);
            }
        }

        private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
        {
            validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
            {
                base.HandleUnauthorizedRequest(filterContext);
            }
            else
            {
                filterContext.Result =
                    new RedirectToRouteResult(
                        new RouteValueDictionary(new {controller = "Error", action = "Unauthorized"}));
            }
        }
    }

这样,Vivien Chevallier 的逻辑就完美运作了

【讨论】:

    【解决方案2】:

    在你看来,你可以这样写:

    @if (User.IsInRole("role"))
    {
        <li>@Html.ActionLink("Words", "View", "Controller")</li>
        <li>@Html.ActionLink("Words", "View", "Controller")</li>
    }
    

    ...假设他们已登录,它将有条件地隐藏链接

    【讨论】:

      【解决方案3】:

      当你用授权属性装饰一个动作或控制器时,该动作只有在用户被授权时才会执行。这意味着如果用户未获得授权,则视图(将包含您所有授权的链接扩展)根本不会呈现。

      因此,您需要将属性中的授权逻辑与 html 扩展的逻辑分开。

      我还注意到,在您的属性的授权核心中,您正在执行以下操作:

      if ((action == "Details") || (action == "Index"))
                  {
                      if (httpContext.User.IsInRole(Constants.DataReader))
                          return true;
                  }
      

      这是非常非常糟糕的主意!您不应在授权核心逻辑中指定操作名称! 您需要做的就是使用具有适当角色的默认授权属性来装饰“详细信息”和“索引”方法:

      [Authorize(Roles=Constants.DataReader)]
      public ActionResult Index()
      {
      }
      

      现在关于角色依赖助手:

      你可以这样做:

      public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string roles, other arguments)
      {
         //assuming that roles are passed as coma separated strings
         var rolesList = roles.Split(",",roles);
         bool shouldShow = false;
         foreach(var role in rolesList )
         {
             if (HttpContext.User.IsInRole(role))
             {
                 shouldShow = true;
                 break;
             }               
         }
         if(shouldShow)
         {
             //return your extension representation 
         }
         else
         {
             //fallback 
         }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-01-23
        • 1970-01-01
        • 1970-01-01
        • 2012-10-27
        • 2014-10-22
        • 2010-10-21
        • 2013-01-12
        相关资源
        最近更新 更多