【问题标题】:Restrict access to a controller based on certain criteria not user authentication根据某些标准而不是用户身份验证限制对控制器的访问
【发布时间】:2014-02-06 20:59:47
【问题描述】:

限制对控制器的访问的正确方法是什么?

例如,我可能有“ProductReviewController”,我希望能够检查此控制器是否可在当前商店中访问并已启用。我不关注执行此操作的代码,但如果不满足此条件,我对阻止用户访问控制器的方法感兴趣。我希望请求继续进行,就好像控制器从未存在过一样(所以可能会抛出 404)。

到目前为止我的想法:

  1. 数据注释,即[IsValidController]。我将从哪个 Attribute 类派生 - Authorize 似乎并不适合,我会将其与用户身份验证相关联。另外,我不确定如果不满足标准,正确的响应是什么(但我想这将取决于它所派生的Attribute)。我可以将此数据注释放在我的基本控制器上。

  2. 如果控制器不符合我的标准,请在页面生命周期的较低位置查找并停止用户点击控制器。即创建我自己的控制器工厂,如第 7 点所述:http://blogs.msdn.com/b/varunm/archive/2013/10/03/understanding-of-mvc-page-life-cycle.aspx

最好的方法是什么?

注意:目前,我倾向于选项 1,并使用AuthorizeAttribute 与下面的代码类似。我觉得我在滥用AuthorizeAttribute

public class IsControllerAccessible : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (!CriteriaMet())
            return false;

        return true;
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.Result = new RedirectToRouteResult(
        new RouteValueDictionary(
            new
            {
                controller = "Generic",
                action = "404"
            })
        );
    }
}

【问题讨论】:

    标签: asp.net-mvc controller data-annotations asp.net-mvc-5


    【解决方案1】:

    我认为您对AuthorizeAttribute 感到困惑。它是一个动作过滤器,而不是数据注释。 Data Annotations 为验证修饰模型属性,Action Filter 修饰控制器动作以检查控制器的上下文并在动作执行之前做一些事情。

    所以,限制对控制器操作的访问是AuthorizeAttribute 存在的理由,所以让我们使用它!

    在 SO 的好人的帮助下,我创建了一个客户操作过滤器,它基于作为访问目录组的一部分来限制对操作(甚至是控制器)的访问:

    public class AuthorizeADAttribute : AuthorizeAttribute
    {
        public string Groups { get; set; }
    
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (base.AuthorizeCore(httpContext))
            {
                /* Return true immediately if the authorization is not 
                locked down to any particular AD group */
                if (String.IsNullOrEmpty(Groups))
                    return true;
    
                // Get the AD groups
                var groups = Groups.Split(',').ToList<string>();
    
                // Verify that the user is in the given AD group (if any)
                var context = new PrincipalContext(ContextType.Domain, "YOURADCONTROLLER");
                var userPrincipal = UserPrincipal.FindByIdentity(context,
                                                     IdentityType.SamAccountName,
                                                     httpContext.User.Identity.Name);
    
                foreach (var group in groups)
                {
                    try
                    {
                        if (userPrincipal.IsMemberOf(context, IdentityType.Name, group))
                            return true;
                    }
                    catch (NoMatchingPrincipalException exc)
                    {
                        var msg = String.Format("While authenticating a user, the operation failed due to the group {0} could not be found in Active Directory.", group);
                        System.ApplicationException e = new System.ApplicationException(msg, exc);
                        ErrorSignal.FromCurrentContext().Raise(e);
                        return false;
                    }
                    catch (Exception exc)
                    {
                        var msg = "While authenticating a user, the operation failed.";
                        System.ApplicationException e = new System.ApplicationException(msg, exc);
                        ErrorSignal.FromCurrentContext().Raise(e);
                        return false;
                    }
                }
            }
            return false;
        }
    }
    

    请注意,这将返回一个 401 Unauthorized,这是有道理的,而不是您在上面指出的 404 Not Found

    现在,其中的神奇之处在于您可以通过在操作级别应用它来限制访问:

    [AuthorizeAD(Groups = "Editor,Contributer")]
    public ActionResult Create()
    

    或者在控制器级别:

    [AuthorizeAD(Groups = "Admin")]
    public class AdminController : Controller
    

    甚至可以通过在 `/App_Start' 中编辑 FilterConfig.cs 来全局编辑:

    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
            filters.Add(new Code.Filters.MVC.AuthorizeADAttribute() { Groups = "User, Editor, Contributor, Admin" });
        }
    

    完成很棒的酱汁!

    附:您在第二点中提到了页面生命周期。在 MVC 中没有这样的东西,至少在您可能认为的 Web 表单意义上没有。这对我来说是件好事,因为事情被大大简化了,而且我不必记住十几个不同的生命周期事件以及每个事件是为了什么而引发的!

    【讨论】:

    • 好的,感谢您对数据注释的澄清 - 我的术语有误。我刚刚使用AuthorizeAttribute 编写了自己的版本,并且运行良好。我只是想确保这是正确的方法。当我提到生命周期时,我指的是这种东西:blogs.msdn.com/b/varunm/archive/2013/10/03/…。我特别考虑覆盖第 7 点并创建一个自定义控制器工厂。
    • p.s 404 not found 在我的情况下是正确的回应。不是未经授权的用户,而是不应该显示内容的网站(这意味着用户点击了缓存的链接或其他内容)。
    • 那么代码可能应该驻留在控制器本身中,因为它负责根据用户交互提供正确的视图。如果用户提供了产品 id,让控制器检查它在商店中是否存在,如果没有,则重定向到指示此类的特定视图。
    猜你喜欢
    • 1970-01-01
    • 2011-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-09
    • 2014-06-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多