【问题标题】:Asp.net MVC4: Authorize on both controller and actionAsp.net MVC4:对控制器和操作进行授权
【发布时间】:2013-05-18 13:44:18
【问题描述】:

如果我在控制器和动作上都有 Authorize 属性,哪个会生效?还是两者都会生效?

【问题讨论】:

标签: asp.net-mvc authorize-attribute


【解决方案1】:

你问:

如果我在控制器和动作上都有 Authorize 属性,哪个会生效?两者都有?

简单地回答这个问题:两者兼而有之。效果是将AND这两个限制在一起。我将在下面解释原因...

详情

所以,你可能会问这个问题有几个原因。

  1. 您想知道与方法相比,如何对 Action 实施额外的约束。例如
    • 在控制器级别,强制用户扮演“用户”角色
    • 在操作级别,另外强制用户扮演“管理员”角色
  2. 您想在操作级别替换控制器约束
  3. 您想在操作级别移除控制器约束,并让匿名用户可以使用该方法

您没有指定您的 MVC 版本,因此我将假定为今天的最新版本 (MVC 4.5)。但是,即使您使用的是 MVC 3,答案也不会有太大变化。

[Anonymous] 覆盖控制器 [Authorize](案例 3)

案例 3。我不需要覆盖([AllowAnonymous] 的使用),因为它已经回答了 all over SOall over the web。可以这么说:如果你在一个动作上指定[AllowAnonymous],即使控制器上有[Authorize],它也会将该动作公开。

您还可以使整个网站受using a global filter 的授权,并在您想要公开的少数操作或控制器上使用AllowAnonymous

[Authorize] 是加法(案例 1)

案例 1 很简单。以如下控制器为例:

[Authorize(Roles="user")]
public class HomeController : Controller {
    public ActionResult AllUsersIndex() {
        return View();
    }

    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
}

默认情况下,[Authorize(Roles="user")] 使控制器中的所有操作仅对“用户”角色的帐户可用。因此,要访问AllUsersIndex,您必须是“用户”角色。但是,要访问AdminUsersIndex,您必须同时拥有“用户”和“管理员”角色。例如:

  • 用户名:Bob,角色:用户,不能访问AdminUsersIndex,但可以访问AllUsersIndex
  • 用户名:Jane,角色:管理员,不能访问AdminUsersIndexAllUsersIndex
  • 用户名:Tim,角色:用户和管理员,可以访问AdminUsersIndexAllUsersIndex

这说明[Authorize] 属性是可加的。属性的Users 属性也是如此,可以将其与Roles 组合以使其更具限制性。

这种行为是由于控制器和动作属性的工作方式造成的。这些属性被链接在一起并应用于订单控制器然后是操作。如果第一个拒绝授权,则控制返回并且不调用操作的属性。如果第一个通过了授权,那么第二个也将被检查。您可以通过指定Order(例如[Authorize(Roles = "user", Order = 2)])来覆盖此顺序。

覆盖[Authorize](案例2)

案例 2 更棘手。回想一下,[Authorize] 属性的检查顺序是(全局然后)控制器然后动作。第一个检测到用户没有资格被授权的获胜,其他人不会被调用。

解决此问题的一种方法是定义两个新属性,如下所示。 [OverrideAuthorize] 除了服从 [Authorize] 之外什么都不做;它的唯一目的是定义一个我们可以检查的类型。 [DefaultAuthorize] 允许我们检查请求中调用的 Action 是否用 [OverrideAuthorize] 装饰。如果是,则我们推迟到 Action 授权检查,否则我们继续进行 Controller 级别检查。

public class DefaultAuthorizeAttribute : AuthorizeAttribute {
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var action = filterContext.ActionDescriptor;
        if (action.IsDefined(typeof(OverrideAuthorizeAttribute), true)) return;

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

然后我们可以这样使用它:

[DefaultAuthorize(Roles="user")]
public class HomeController : Controller {
    // Available to accounts in the "user" role
    public ActionResult AllUsersIndex() {
        return View();
    }
    // Available only to accounts both in the "user" and "admin" role
    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
    // Available to accounts in the "superuser" role even if not in "user" role
    [OverrideAuthorize(Roles = "superuser")]
    public ActionResult SuperusersIndex() {
        return View();
    }
}

在上面的示例中,SuperusersIndex 可用于具有“超级用户”角色的帐户,即使它没有“用户”角色。

【讨论】:

  • 和我的没有区别。有一个属性范围。授权应该转移到第一个大坝。这与if(condition1&&condition2) 之类的比较逻辑相同,如果第一个条件为假,则编译器不查看第二个条件并跳过 if-block。
  • 感谢您提供非常详细的信息,这是我问这个问题时想了解的。谢谢!
  • “用户”角色是否有权访问SuperusersIndex 操作方法?
  • 没有。 OverrideAuthorize 凭借DefaultAuthorizeAttribute.OnAuthorization 中的if (action.IsDefined(typeof(OverrideAuthorizeAttribute), true)) return; 完全替代了DefaultAuthorize 定义的默认权限
  • 在 MVC5 中,案例 2 可以简单地通过将 [OverrideAuthorization] 属性添加到操作中来完成,如 this answer 中所述。
【解决方案2】:

我想在 Overriding [Authorize] 中添加一些内容(案例 2)

OverrideAuthorizeAttribute 和 DefaultAuthorizeAttribute 工作正常,但我发现你也可以使用 OverrideAuthorizationAttribute 覆盖在更高级别定义的授权过滤器。

[Authorize(Roles="user")]
public class HomeController : Controller {
    // Available to accounts in the "user" role
    public ActionResult AllUsersIndex() {
        return View();
    }
    // Available only to accounts both in the "user" and "admin" role
    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
    // Available to accounts in the "superuser" role even if not in "user" role
    [OverrideAuthorization()]
    [Authorize(Roles = "superuser")]
    public ActionResult SuperusersIndex() {
        return View();
    }
}

【讨论】:

  • 但这不适用于自定义 AuthorizeAttribute 对吧?
  • 需要 MVC 5。
  • 它帮助了我。但请在答案中添加 MVC5,以防人们错过。
【解决方案3】:

如果在控制器上使用,则该控制器的所有方法都会生效。

[Authorize]
public class SomeController(){

    // all actions are effected
    public ActionResult Action1
    public ActionResult Action2

如果你想阻止这些操作之一,你可以使用这样的东西:

[Authorize]
public class SomeController(){

    // all actions are effected
    public ActionResult Action1
    public ActionResult Action2

    [AllowAnonymous]
    public ActionResult Action3 // only this method is not effected...

【讨论】:

  • OP问如果你把[Authorize]放在控制器和动作“哪个会生效?两个?”上会发生什么,你还没有回答那个。
  • @Andy Brown,所以,如果他在控制器上定义它,那么不需要在这个控制器的任何动作上写它。不必要在动作上使用它。你可以从我的回答中理解。我的意思是也不需要解释。
  • 我想如果你看清楚答案,他已经回答了。因为他说所有方法都会受到影响,这意味着如果你把它放在一个动作上也没关系。
  • @NickN。我的观点是它可以很重要。 Authorize 属性链在一起。如果第一个允许用户,第二个仍然可以阻止用户。另外问题是关于两个Authorize 属性(一个在控制器上,一个在动作上),而不是Authorize 然后AllowAnonymous。我添加了另一个答案来说明我的观点。
  • @AndyBrown 我同意你的看法,现在我已经看到你的回答了。
【解决方案4】:

我为 ASP.NET Core 2.1 改编了 this answer's second case

与 ASP.NET Core 的 AuthorizeAttribute 的不同之处在于您不必调用 AuthorizeAttribute.OnAuthorization 基本方法即可进行正常授权。这意味着即使您没有显式调用基础方法,基础AuthorizeAttribute 仍然可以通过禁止访问来短路授权。

我所做的是我创建了一个DefaultAuthorizeAttribute,它不是继承自AuthorizeAttribute,而是继承自Attribute。由于DefaultAuthorizeAttribute 没有从AuthorizeAttribute 继承,我不得不重新创建授权行为。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class DefaultAuthorizeAttribute : Attribute, IAuthorizationFilter
{
    private readonly AuthorizeFilter m_authorizeFilter;

    public DefaultAuthorizeAttribute(params string[] authenticationSchemes)
    {
        var policyBuilder = new AuthorizationPolicyBuilder()
            .AddAuthenticationSchemes(authenticationSchemes)
            .RequireAuthenticatedUser();
        m_authorizeFilter = new AuthorizeFilter(policyBuilder.Build());
    }

    public void OnAuthorization(AuthorizationFilterContext filterContext)
    {
        if (filterContext.ActionDescriptor is ControllerActionDescriptor controllerAction
            && controllerAction.MethodInfo.GetCustomAttributes(typeof(OverrideAuthorizeAttribute), true).Any())
        {
            return;
        }
        m_authorizeFilter.OnAuthorizationAsync(filterContext).Wait();
    }
}

public class OverrideAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext filterContext) { }
}

【讨论】:

  • 这不起作用。看看OnAuthorizationAsync 的实现——GetEffectivePolicyAsync 总是返回 null 因为GetEffectivePolicyAsync 只适用于实际应用于控制器/动作的过滤器,而我们的授权过滤器不是。我们只保留在m_authorizeFilter...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-07
相关资源
最近更新 更多