ASP.NET WebAPI中的自定义授权 - 一团糟?

124

我正在阅读几个资源(书籍和SO答案)关于WebApi中的授权。

假设我想添加自定义属性,仅允许特定用户访问:

情况#1

我看到了这种方法,即覆盖 OnAuthorization ,如果出现问题,则设置响应。

public class AllowOnlyCertainUsers : AuthorizeAttribute
{
 public override void OnAuthorization(HttpActionContext actionContext)
  {
   if ( /*check if user OK or not*/)
   {
     actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
   }
  }
}

案例 #2

但我也见过类似的例子,它同样重写了 OnAuthorization 方法,但是调用了 base

public override void OnAuthorization(HttpActionContext actionContext) 
{ 
  base.OnAuthorization(actionContext);

    // If not authorized at all, don't bother

    if (actionContext.Response == null)  
     {
      //...
     }
}

然后,您需要检查HttpActionContext.Response是否已设置。如果未设置,则表示请求已获得授权且用户已通过验证。

情况#3

但我也看到了覆盖IsAuthorized的这种方法:

public class AllowOnlyCertainUsers : AuthorizeAttribute
{
 protected override bool IsAuthorized(HttpActionContext context)
  {
   if ( /*check if user OK or not*/)
   {
    return true;// or false
   }
  }
}

案例 #4

然后我看到了一个类似的例子,但是通过调用base.IsAuthorized(context):

protected override bool IsAuthorized(HttpActionContext context)
{
 if (something1 && something2 && base.IsAuthorized(context)) //??
 return true;
 return false;
}

还有一件事

最后,Dominick在这里说:

你不应该重写OnAuthorization - 因为你会错过[AllowAnonymous]处理。

问题

  • 1)我应该使用哪些方法:IsAuthorizedOnAuthorization?(或者何时使用哪个)

  • 2)何时调用base.IsAuthorizedbase.OnAuthorization

  • 3)他们是这样构建的吗?如果响应为空,则一切正常吗?(情况#2)

NB

请注意,我仅使用(并希望使用)AuthorizeAttribute,它已经继承了AuthorizationFilterAttribute

为什么?

因为我处于第一阶段:http://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api

enter image description here

无论如何,我是通过扩展Authorize属性来提问的。


你需要覆盖授权属性吗?您想要实现哪种用例?如果您需要允许特定用户访问,为什么不使用[Authorize(Users =“Admin”)]属性呢? - Taiseer Joudeh
1
例如,尝试在10:00到12:00之间(可配置)授权用户。您无法使用普通角色和授权属性来实现此目的,必须自己编写逻辑。 - Royi Namir
3个回答

102
哪些方法应该使用:IsAuthorized还是OnAuthorization?(或者何时使用哪个)
如果您的授权逻辑不依赖于已建立的身份和角色,则可以扩展AuthorizationFilterAttribute。对于与用户相关的授权,您将扩展并使用AuthorizeAttribute。对于前一种情况,您将覆盖OnAuthorization。对于后一种情况,您将覆盖IsAuthorized。从这些属性的源代码中可以看出,如果您从AuthorizationFilterAttribute派生,则OnAuthorization标记为虚拟以供您覆盖。另一方面,在AuthorizeAttribute中,IsAuthorized方法被标记为虚拟的。我相信这是使用意图的一个好提示。
何时调用base.IsAuthorized或base.OnAuthorization?
这个问题的答案在于OO通常如何工作。如果您覆盖了一个方法,您可以完全提供一个新实现,也可以基于父类提供的实现进行增强。例如,以IsAuthorized(HttpActionContext)为例。基类行为是检查用户/角色是否符合过滤器中指定的内容与已建立的身份。假设,您想要做所有这些,但是另外还想检查其他东西,比如基于请求头之类的内容。在这种情况下,您可以提供如下覆盖。
protected override bool IsAuthorized(HttpActionContext actionContext)
{
    bool isAuthroized = base.IsAuthorized(actionContext);
    // Here you look at the header and do your additional stuff based on actionContext
    // and store the result in isRequestHeaderOk
    // Then, you can combine the results
    // return isAuthorized && isRequestHeaderOk;
}

很抱歉,我不明白你的Q3问题。顺便说一句,授权过滤器已经存在很长时间了,人们用它来做各种事情,有时候使用不当。

还有一件事。最后有个人在这里说:你不应该重写OnAuthorization - 因为你会错过[AllowAnonymous]处理。

那个说话的人是访问控制之神Dominick。显然他是正确的。如果你查看下面的OnAuthorization实现(复制如下),

public override void OnAuthorization(HttpActionContext actionContext)
{
    if (actionContext == null)
    {
        throw Error.ArgumentNull("actionContext");
    }

    if (SkipAuthorization(actionContext))
    {
        return;
    }

    if (!IsAuthorized(actionContext))
    {
        HandleUnauthorizedRequest(actionContext);
    }
}

SkipAuthorization方法调用确保应用了AllowAnonymous过滤器,即跳过了授权。如果您重写此方法,您将失去这种行为。实际上,如果您决定基于用户/角色进行授权,那么此时您将决定从AuthorizeAttribute派生。此时您唯一正确的选择是覆盖IsAuthorized而不是已经被覆盖的OnAuthorization,尽管技术上可以两个都覆盖。

PS. 在ASP.NET Web API中,还有另一个称为身份验证过滤器的过滤器。其想法是您使用该过滤器进行身份验证,并使用授权过滤器进行授权,如其名称所示。但是,有许多示例混淆了这种边界。很多授权过滤器示例都会进行某种形式的身份验证。无论如何,如果您有时间并且想更好地理解,可以查看这篇MSDN文章。免责声明:本文是由我编写的。


当然可以,如果这是你想要的。 - Badri
是的,我被另一本书搞混了。我同时在阅读三本书:安全(你的),实用(你的)和WebAPI Pro(Tugberk's,Zeitler,Ali)。正如你所看到的 - 他们在这里做了:http://i.stack.imgur.com/LNGi4.jpg - 他们只是检查了是否为空,那么我应该检查空还是错误代码? - Royi Namir
Badri你好,我在你的书中找到了这个问题:为什么设置context.Response会结束请求?不要误解我的意思,它确实会结束请求,但是在哪里写明了呢?(文档、代码、其他什么东西...)PS它与我的第三个问题有关(检查if (actionContext.Response == null)...)。我也在这里看到了它(https://dev59.com/2mQm5IYBdhLWcg3w0Bq5#17317849)-似乎设置Response会停止一切并输出响应。你能否请解释一下? - Royi Namir
你们能不能帮我看一下这里的帖子,我在实现这个自定义 WebAPI 授权属性时遇到了麻烦:https://dev59.com/tpTfa4cB1Zd3GeqPMB2L - Luis Valencia
你好,你提到 base.isAuthorized 会检查用户是否符合“过滤器”,那么这个过滤器是什么,我该如何指定它呢? - Chara
显示剩余9条评论

18

如果您正在使用 OAuth bearer 令牌保护 Web API,并且在发放令牌时为用户设置了 allowedTime 声明,那么我的建议是执行以下操作。您可以在此处阅读有关基于令牌的身份验证的更多信息。

  1. 创建 CustomAuthorizeAttribute,该属性从 AuthorizationFilterAttribute 派生
  2. 重写 OnAuthorizationAsync 方法并使用下面的示例代码:

 public class CustomAuthorizeAttribute : AuthorizationFilterAttribute
{

    public override Task OnAuthorizationAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
    {

        var principal = actionContext.RequestContext.Principal as ClaimsPrincipal;

        if (!principal.Identity.IsAuthenticated)
        {
            return Task.FromResult<object>(null);
        }

        var userName = principal.FindFirst(ClaimTypes.Name).Value;
        var userAllowedTime = principal.FindFirst("userAllowedTime").Value;

        if (currentTime != userAllowedTime)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "Not allowed to access...bla bla");
            return Task.FromResult<object>(null);
        }

        //User is Authorized, complete execution
        return Task.FromResult<object>(null);

    }
}
  • 现在你可以在控制器中使用CustomAuthorize属性来保护控制器,使用此授权逻辑。

  • 1
    谢谢。但是我目前正在使用AuthorizeAttribute,它继承了AuthorizationFilterAttribute,并且为了学习,我特别询问了应该使用哪种方法以及响应是否具有内容的问题... - Royi Namir

    5

    ASP.NET v5推出了全新的授权系统。 对于那些要使用.NET 5的人,我建议转向Microsoft.AspNet.Authorization。

    它基本上解决了保留System.Web.Http.AuthorizeSystem.Web.Mvc.Authorize以及其他旧的身份验证实现所造成的混乱。

    它提供了一个非常好的行动类型(创建、读取、更新、删除)、资源、角色、声明、视图、自定义需求的抽象,并允许构建自定义处理程序,组合上述任何内容。此外,这些处理程序也可以组合使用。

    在ASP.NET v5中,授权现在提供了简单的声明式角色和更丰富的基于策略的模型,其中授权表达为需求,处理程序评估用户的声明与需求相对应。命令式检查可以基于简单的策略或同时评估用户身份和用户试图访问的资源的属性的策略。


    15
    知道这点很好,但完全没有回答问题。 - Zero3

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接