Authentication and Authorization in ASP.NET Web API(ASP.NET Web API 中的身份验证和授权)

作者:Rick Anderson

你已创建 Web API,但现在想要控制对它的访问。 在本系列文章中,我们将介绍一些用于保护 Web API 免受未经授权的用户保护的选项。 本系列将介绍身份验证和授权。

  • 身份验证 是知道用户的标识。 例如,Alice 使用她的用户名和密码登录,服务器使用该密码对 Alice 进行身份验证。
  • 授权 决定是否允许用户执行操作。 例如,Alice 有权获取资源,但无权创建资源。

本系列的第一篇文章概述了 ASP.NET Web API中的身份验证和授权。 其他主题介绍 Web API 的常见身份验证方案。

注意

感谢回顾本系列并提供宝贵反馈的人:里克·安德森、利维·布罗德里克、巴里·多兰斯、汤姆·戴克斯特拉、葛红梅、大卫·马特森、丹尼尔·罗斯、蒂姆·泰布肯。

身份验证

Web API 假定身份验证在主机中进行。 对于 Web 托管,主机是 IIS,它使用 HTTP 模块进行身份验证。 可以将项目配置为使用内置于 IIS 或 ASP.NET 的任何身份验证模块,或者编写自己的 HTTP 模块来执行自定义身份验证。

当主机对用户进行身份验证时,它会创建一个主体,该 主体是一个 IPrincipal 对象,表示运行代码的安全上下文。 主机通过设置 Thread.CurrentPrincipal 将主体附加到当前线程。 主体包含关联的 Identity 对象,该对象包含有关用户的信息。 如果用户已经过身份验证, Identity.IsAuthenticated 属性将返回 true。 对于匿名请求, IsAuthenticated 返回 false。 有关主体的详细信息,请参阅 基于角色的安全性

用于身份验证的 HTTP 消息处理程序

可以将身份验证逻辑放入 HTTP 消息处理程序中,而不是使用主机进行身份验证。 在这种情况下,消息处理程序会检查 HTTP 请求并设置主体。

何时应使用消息处理程序进行身份验证? 下面是一些权衡:

  • HTTP 模块可查看通过 ASP.NET 管道的所有请求。 消息处理程序仅查看路由到 Web API 的请求。
  • 可以设置每路由消息处理程序,以便将身份验证方案应用于特定路由。
  • HTTP 模块特定于 IIS。 消息处理程序与主机无关,因此它们可以与 Web 托管和自承载一起使用。
  • HTTP 模块参与 IIS 日志记录、审核等。
  • HTTP 模块在管道中提前运行。 如果在消息处理程序中处理身份验证,则在处理程序运行之前不会设置主体。 此外,当响应离开消息处理程序时,主体将还原回上一个主体。

通常,如果不需要支持自承载,则 HTTP 模块是一个更好的选择。 如果需要支持自承载,请考虑使用消息处理程序。

设置主体

如果应用程序执行任何自定义身份验证逻辑,则必须在两个位置设置主体:

  • Thread.CurrentPrincipal。 此属性是在 .NET 中设置线程主体的标准方法。
  • HttpContext.Current.User。 此属性特定于 ASP.NET。

以下代码演示如何设置主体:

private void SetPrincipal(IPrincipal principal)
{
    Thread.CurrentPrincipal = principal;
    if (HttpContext.Current != null)
    {
        HttpContext.Current.User = principal;
    }
}

对于 Web 托管,必须在这两个位置设置主体;否则,安全上下文可能会变得不一致。 但是,对于自承载, HttpContext.Current 为 null。 因此,为了确保代码与主机无关,在分配给 HttpContext.Current 之前,检查 null,如下所示。

授权

授权稍后在管道中发生,离控制器更近。 这允许你在授予对资源的访问权限时做出更精细的选择。

  • 授权筛选器 在控制器操作之前运行。 如果请求未获授权,筛选器将返回错误响应,并且不会调用该操作。
  • 在控制器操作中,可以从 ApiController.User 属性获取当前主体。 例如,可以根据用户名筛选资源列表,仅返回属于该用户的资源。

身份验证和授权管道示意图。

使用 [Authorize] 属性

Web API 提供内置的授权筛选器 AuthorizeAttribute。 此筛选器检查用户是否已通过身份验证。 否则,它将返回 HTTP 状态代码 401 (未授权) ,而不调用操作。

可以在全局、控制器级别或单个操作级别应用筛选器。

全局:若要限制每个 Web API 控制器的访问,请将 AuthorizeAttribute 筛选器添加到全局筛选器列表:

public static void Register(HttpConfiguration config)
{
    config.Filters.Add(new AuthorizeAttribute());
}

控制器:若要限制对特定控制器的访问,请将筛选器作为属性添加到控制器:

// Require authorization for all actions on the controller.
[Authorize]
public class ValuesController : ApiController
{
    public HttpResponseMessage Get(int id) { ... }
    public HttpResponseMessage Post() { ... }
}

操作:若要限制对特定操作的访问,请将 属性添加到操作方法:

public class ValuesController : ApiController
{
    public HttpResponseMessage Get() { ... }

    // Require authorization for a specific action.
    [Authorize]
    public HttpResponseMessage Post() { ... }
}

或者,可以使用 属性限制控制器, [AllowAnonymous] 然后允许匿名访问特定操作。 在以下示例中 Post , 方法受到限制,但 Get 方法允许匿名访问。

[Authorize]
public class ValuesController : ApiController
{
    [AllowAnonymous]
    public HttpResponseMessage Get() { ... }

    public HttpResponseMessage Post() { ... }
}

在前面的示例中, 筛选器允许任何经过身份验证的用户访问受限制的方法;只有匿名用户被排除在外。还可以将访问权限限制为特定用户或具有特定角色的用户:

// Restrict by user:
[Authorize(Users="Alice,Bob")]
public class ValuesController : ApiController
{
}
   
// Restrict by role:
[Authorize(Roles="Administrators")]
public class ValuesController : ApiController
{
}

注意

Web API 控制器的 AuthorizeAttribute 筛选器位于 System.Web.Http 命名空间中。 System.Web.Mvc 命名空间中有一个类似的 MVC 控制器筛选器,该筛选器与 Web API 控制器不兼容。

自定义授权筛选器

若要编写自定义授权筛选器,请派生自以下类型之一:

  • AuthorizeAttribute。 扩展此类以基于当前用户和用户的角色执行授权逻辑。
  • AuthorizationFilterAttribute。 扩展此类以执行不一定基于当前用户或角色的同步授权逻辑。
  • IAuthorizationFilter。 实现此接口以执行异步授权逻辑;例如,如果授权逻辑进行异步 I/O 或网络调用。 (如果授权逻辑受 CPU 限制,则从 AuthorizationFilterAttribute 派生会更简单,因为这样就不需要编写异步方法)

下图显示了 AuthorizeAttribute 类的类层次结构。

Authorize Attribute 类的类层次结构示意图。

Authorize Attribute 类的类层次结构示意图。 授权属性位于底部,顶部有一个箭头指向“授权筛选器属性”,一个箭头指向“I 授权筛选器”。

控制器操作中的授权

在某些情况下,可以允许请求继续,但会根据主体更改行为。 例如,返回的信息可能会根据用户的角色而更改。 在控制器方法中,可以从 ApiController.User 属性获取当前主体。

public HttpResponseMessage Get()
{
    if (User.IsInRole("Administrators"))
    {
        // ...
    }
}