根据特定条件而非用户身份验证限制对控制器的访问

时间:2014-02-06 20:59:47

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

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

例如,我可能有“ProductReviewController”,我希望能够检查此控制器是否可以在当前存储中访问并启用。我不是在代码之后这样做,但是如果不满足这个标准,我对停止用户进入控制器的方法很感兴趣。我希望这个请求可以继续进行,好像控制器永远不会存在(所以可能会抛出404)。

到目前为止我的想法:

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

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

  3. 最佳方法是什么?

    注意:目前,我倾向于选项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"
                })
            );
        }
    }
    

1 个答案:

答案 0 :(得分:3)

我认为你对AuthorizeAttribute感到困惑。它是一个动作过滤器,而不是数据注释。 Data Annotations为validatioj修饰模型属性,Action Filter的装饰控制器操作用于检查控制器的上下文并在操作执行之前执行某些操作。

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

在SO的优秀人员的帮助下,我创建了一个客户操作过滤器,它基于成为Access Directory组的一部分,限制对操作(甚至控制器)的访问:

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" });
    }

完成很棒的酱汁!

P.S。你在第二点提到页面生命周期。在MVC中没有这样的东西,至少在你可能在想的Web Forms意义上是这样的。这对我来说是一件好事,因为事情已经大大简化了,而且我不必记住十几个不同的生命周期事件以及它们中的每一个都被提出来了!