Autenticação e autorização no ASP.NET Web API

por Rick Anderson

Você criou uma API Web, mas agora deseja controlar o acesso a ela. Nesta série de artigos, examinaremos algumas opções para proteger uma API Web de usuários não autorizados. Esta série abordará a autenticação e a autorização.

  • A autenticação é conhecer a identidade do usuário. Por exemplo, Alice faz logon com seu nome de usuário e senha, e o servidor usa a senha para autenticar Alice.
  • A autorização está decidindo se um usuário tem permissão para executar uma ação. Por exemplo, Alice tem permissão para obter um recurso, mas não criar um recurso.

O primeiro artigo da série fornece uma visão geral da autenticação e autorização no ASP.NET Web API. Outros tópicos descrevem cenários comuns de autenticação para a API Web.

Observação

Graças às pessoas que revisaram esta série e forneceram comentários valiosos: Rick Anderson, Levi Broderick, Barry Dorrans, Tom Dykstra, Hongmei Ge, David Matson, Daniel Roth, Tim Teebken.

Autenticação

A API Web pressupõe que a autenticação ocorra no host. Para hospedagem na Web, o host é o IIS, que usa módulos HTTP para autenticação. Você pode configurar seu projeto para usar qualquer um dos módulos de autenticação integrados ao IIS ou ASP.NET ou escrever seu próprio módulo HTTP para executar a autenticação personalizada.

Quando o host autentica o usuário, ele cria uma entidade de segurança, que é um objeto IPrincipal que representa o contexto de segurança sob o qual o código está sendo executado. O host anexa a entidade de segurança ao thread atual definindo Thread.CurrentPrincipal. A entidade de segurança contém um objeto Identity associado que contém informações sobre o usuário. Se o usuário for autenticado, a propriedade Identity.IsAuthenticated retornarátrue. Para solicitações anônimas, IsAuthenticated retorna false. Para obter mais informações sobre entidades de segurança, consulte Segurança baseada em função.

Manipuladores de mensagens HTTP para autenticação

Em vez de usar o host para autenticação, você pode colocar a lógica de autenticação em um manipulador de mensagens HTTP. Nesse caso, o manipulador de mensagens examina a solicitação HTTP e define a entidade de segurança.

Quando você deve usar manipuladores de mensagens para autenticação? Aqui estão algumas compensações:

  • Um módulo HTTP vê todas as solicitações que passam pelo pipeline de ASP.NET. Um manipulador de mensagens vê apenas solicitações roteada para a API Web.
  • Você pode definir manipuladores de mensagens por rota, o que permite aplicar um esquema de autenticação a uma rota específica.
  • Os módulos HTTP são específicos do IIS. Os manipuladores de mensagens são independentes de host, portanto, podem ser usados com hospedagem na Web e auto-hospedagem.
  • Os módulos HTTP participam do registro em log, auditoria e assim por diante do IIS.
  • Os módulos HTTP são executados anteriormente no pipeline. Se você manipular a autenticação em um manipulador de mensagens, a entidade de segurança não será definida até que o manipulador seja executado. Além disso, a entidade de segurança volta para a entidade de segurança anterior quando a resposta deixa o manipulador de mensagens.

Em geral, se você não precisar dar suporte à auto-hospedagem, um módulo HTTP será uma opção melhor. Se você precisar dar suporte à auto-hospedagem, considere um manipulador de mensagens.

Configurando a entidade de segurança

Se o aplicativo executar qualquer lógica de autenticação personalizada, você deverá definir a entidade de segurança em dois locais:

  • Thread.CurrentPrincipal. Essa propriedade é a maneira padrão de definir a entidade de segurança do thread no .NET.
  • HttpContext.Current.User. Essa propriedade é específica para ASP.NET.

O código a seguir mostra como definir a entidade de segurança:

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

Para hospedagem na Web, você deve definir a entidade de segurança em ambos os locais; caso contrário, o contexto de segurança pode se tornar inconsistente. No entanto, para auto-hospedagem, HttpContext.Current é nulo. Para garantir que seu código seja independente de host, portanto, marcar para nulo antes de atribuir a HttpContext.Current, conforme mostrado.

Autorização

A autorização ocorre mais tarde no pipeline, mais perto do controlador. Isso permite que você faça escolhas mais granulares ao conceder acesso aos recursos.

  • Os filtros de autorização são executados antes da ação do controlador. Se a solicitação não estiver autorizada, o filtro retornará uma resposta de erro e a ação não será invocada.
  • Em uma ação do controlador, você pode obter a entidade de segurança atual da propriedade ApiController.User . Por exemplo, você pode filtrar uma lista de recursos com base no nome de usuário, retornando apenas os recursos que pertencem a esse usuário.

Diagrama do pipeline de autenticação e autorização.

Usando o atributo [Authorize]

A API Web fornece um filtro de autorização interno, AuthorizeAttribute. Esse filtro verifica se o usuário está autenticado. Caso contrário, ele retornará HTTP status código 401 (Não autorizado), sem invocar a ação.

Você pode aplicar o filtro globalmente, no nível do controlador ou no nível de ações individuais.

Globalmente: para restringir o acesso a cada controlador de API Web, adicione o filtro AuthorizeAttribute à lista de filtros global:

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

Controlador: para restringir o acesso a um controlador específico, adicione o filtro como um atributo ao controlador:

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

Ação: para restringir o acesso a ações específicas, adicione o atributo ao método de ação:

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

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

Como alternativa, você pode restringir o controlador e permitir o acesso anônimo a ações específicas usando o [AllowAnonymous] atributo . No exemplo a seguir, o Post método é restrito, mas o Get método permite acesso anônimo.

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

    public HttpResponseMessage Post() { ... }
}

Nos exemplos anteriores, o filtro permite que qualquer usuário autenticado acesse os métodos restritos; somente usuários anônimos são mantidos fora. Você também pode limitar o acesso a usuários específicos ou a usuários em funções específicas:

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

Observação

O filtro AuthorizeAttribute para controladores de API Web está localizado no namespace System.Web.Http . Há um filtro semelhante para controladores MVC no namespace System.Web.Mvc , que não é compatível com controladores de API Web.

Filtros de autorização personalizados

Para escrever um filtro de autorização personalizado, derive de um destes tipos:

  • AuthorizeAttribute. Estenda essa classe para executar a lógica de autorização com base no usuário atual e nas funções do usuário.
  • AuthorizationFilterAttribute. Estenda essa classe para executar a lógica de autorização síncrona que não é necessariamente baseada no usuário ou função atual.
  • IAuthorizationFilter. Implemente essa interface para executar a lógica de autorização assíncrona; por exemplo, se a lógica de autorização fizer chamadas de rede ou de E/S assíncronas. (Se a lógica de autorização estiver associada à CPU, será mais simples derivar de AuthorizationFilterAttribute, pois não será necessário escrever um método assíncrono.)

O diagrama a seguir mostra a hierarquia de classes para a classe AuthorizeAttribute .

Diagrama da hierarquia de classes para a classe Authorize Attribute.

Diagrama da hierarquia de classes para a classe Authorize Attribute. Authorize Attribute está na parte inferior, com uma seta apontando para o Atributo de Filtro de Autorização e uma seta apontando para i Filtro de Autorização na parte superior.

Autorização dentro de uma ação do controlador

Em alguns casos, você pode permitir que uma solicitação prossiga, mas altere o comportamento com base na entidade de segurança. Por exemplo, as informações retornadas podem ser alteradas dependendo da função do usuário. Dentro de um método de controlador, você pode obter a entidade de segurança atual da propriedade ApiController.User .

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