如何使 ASP.NET Web API 正确响应 403 或 401?

Posted

技术标签:

【中文标题】如何使 ASP.NET Web API 正确响应 403 或 401?【英文标题】:How to make ASP.NET Web API respond 403 or 401 appropriately? 【发布时间】:2013-03-21 23:48:35 【问题描述】:

我正在使用 ASP.NET Web API。而且我确实喜欢添加属性以指定对 API 控制器的访问级别的能力,如下所示:

[Authorize]
public IEnumerable<Activity> Get()

到目前为止一切都很好,但是当我使用角色时,这个概念就崩溃了。

[Authorize(Roles = "Manager")]
public IEnumerable<Activity> Get()

我的用户可能在不久前登录了系统,然后在某个时候他们点击了对他们“禁止”的资源。用户尝试再次登录是没有意义的。由于他们的合法帐户无权访问该 URL。但目前他们得到的是 401(未经授权)而不是 403(禁止),就好像他们用错误的帐户登录一样。但用户只有一个帐号,并不打算让用户申请属于其他人的帐号。

还有其他人处理过这个问题吗?有谁知道如何解决这一问题?我非常愿意编写代码来解决这个问题,但我目前不知道从哪里开始。

【问题讨论】:

【参考方案1】:

阅读 Parv 的建议,我创建了以下名为 [WebApiAuthorize] 的自定义过滤器。

关键是 HandleUnauthorizedRequest() 方法。当代码在这个方法中执行时,这是因为用户“出于某种原因”未经授权......所以现在我们只需确定“为什么”......然后:

    为默认行为调用基方法(返回 401).... 或.... 使用 403 返回我们自己的响应。

如您所见,它在适当的时候返回 403(经过身份验证,但未授权)。

public class WebApiAuthorizeAttribute : AuthorizeAttribute

    protected override void HandleUnauthorizedRequest(HttpActionContext ctx)
    
        if (!ctx.RequestContext.Principal.Identity.IsAuthenticated)
            base.HandleUnauthorizedRequest(ctx);
        else
        
            // Authenticated, but not AUTHORIZED.  Return 403 instead!
            ctx.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
        
    

要使用,只需将自定义过滤器扔到控制器或类似这样的操作上.....

[WebApiAuthorize(Roles = "YourRoleA,YourRoleB")]
public class AdminController : ApiController

    public List<Admin> GetAdmins()
    
        ...
    
 

【讨论】:

小改进。您应该检查 Principal 或 Identity 是否为 null,否则当请求未提供凭据时,您可能会收到 NullReferenceException。protected bool IsAuthenticated(HttpActionContext ctx) var principal = ctx.RequestContext.Principal; return principal != null &amp;&amp; principal.Identity != null &amp;&amp; principal.Identity.IsAuthenticated; 【参考方案2】:

我做了一些研究,并为我编写了一个解决方案。我在 System.Web.Mvc 上找到了两个不同的 Authorize 属性,在 System.Web.Http 上找到了第二个。第一个适用于常规 MVC4 应用程序,第二个适用于用于 Web 服务的 MVC4 的 WebAPI 部分,包括 RESTful 接口。所以我用了第二个。

我决定查看 codeplex 的 Authorize Attribute Source 代码。我发现了这个:

    protected virtual bool IsAuthorized(HttpActionContext actionContext)
    
        if (actionContext == null)
        
            throw Error.ArgumentNull("actionContext");
        

        IPrincipal user = Thread.CurrentPrincipal;
        if (user == null || !user.Identity.IsAuthenticated)
        
            return false;
        

        if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
        
            return false;
        

        if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
        
            return false;
        

        return true;
    

很容易看出身份验证和访问是如何混为一谈的,因为它们都具有返回 false 的相同效果。

这是我写的new AuthorizeAttribute,当用户或角色不匹配时返回 403。这样您就可以避免获得本机登录窗口。它包括以下代码。

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

        if (!IsAllowed(actionContext))
        
            HandleForbiddenRequest(actionContext);
        

【讨论】:

不情愿地投了赞成票;您应该在此处引用HandleUnathorizedRequest() 和/或HandleForbiddenRequest() 的代码,这样我们就不必按照您的要点链接。【参考方案3】:

你可以做一个自定义Authorize attribute 然后处理 AuthorizeCoreOnAuthorizationFailed 在后一个中,您可以发送您喜欢的任何回复

有一个look here

【讨论】:

修改 AuthorizeAttribue 就是答案。该特定示例是修改 System.Web.Mvc 而不是 System.Web.Http,因为我使用的是 WebAPI。 我以为你想创建一个自定义属性来发回你喜欢的 Http 状态代码 这就是我所做的,我继承自 System.Web.Http.AuthorizeAtribute。不是来自 System.Web.Mvc.AuthorizeAtribute ,这是您指出的文章所指的内容。 如果您想处理请求授权失败后发送的自定义代码,或者您可以从 AutorizeFilterAttribute 继承

以上是关于如何使 ASP.NET Web API 正确响应 403 或 401?的主要内容,如果未能解决你的问题,请参考以下文章

如何自定义 ASP .NET Web API JWT 令牌响应?

如何使 ASP.NET Web API Web 服务在本地网络中可用

如何使 ASP.NET Core Web API 操作异步执行?

使用 Owin 时无法使 ASP.NET Web API 2 帮助页面正常工作

如何在 Asp.Net Web API 2 中使用 Owin OAuth2 修改令牌端点响应正文

如何通过 ASP.NET Core 3 中的中间 Web API 传递未更改的 HTTP 响应?