Udostępnij za pośrednictwem


Uwierzytelnianie i autoryzacja w interfejsie API sieci Web ASP.NET

Autor: Rick Anderson

Utworzono internetowy interfejs API, ale teraz chcesz kontrolować dostęp do niego. W tej serii artykułów przyjrzymy się niektórym opcjom zabezpieczania internetowego interfejsu API przed nieautoryzowanymi użytkownikami. Ta seria obejmuje zarówno uwierzytelnianie, jak i autoryzację.

  • Uwierzytelnianie zna tożsamość użytkownika. Na przykład Alice loguje się przy użyciu nazwy użytkownika i hasła, a serwer używa hasła do uwierzytelniania Alicji.
  • Autoryzacja decyduje, czy użytkownik może wykonać akcję. Na przykład Alicja ma uprawnienia do pobierania zasobu, ale nie tworzy zasobu.

Pierwszy artykuł z serii zawiera ogólne omówienie uwierzytelniania i autoryzacji w ASP.NET internetowym interfejsie API. W innych tematach opisano typowe scenariusze uwierzytelniania dla internetowego interfejsu API.

Uwaga

Dzięki ludziom, którzy przejrzeli tę serię i przekazali cenne opinie: Rick Anderson, Levi Broderick, Barry Dorrans, Tom Dykstra, Hongmei Ge, David Matson, Daniel Roth, Tim Teebken.

Authentication

Internetowy interfejs API zakłada, że uwierzytelnianie odbywa się na hoście. W przypadku hostingu internetowego host jest usługą IIS, która używa modułów HTTP do uwierzytelniania. Projekt można skonfigurować tak, aby korzystał z dowolnego modułu uwierzytelniania wbudowanego w usługi IIS lub ASP.NET albo napisać własny moduł HTTP w celu przeprowadzenia uwierzytelniania niestandardowego.

Gdy host uwierzytelnia użytkownika, tworzy podmiot zabezpieczeń , który jest obiektem IPrincipal reprezentującym kontekst zabezpieczeń, w którym jest uruchomiony kod. Host dołącza jednostkę do bieżącego wątku, ustawiając wartość Thread.CurrentPrincipal. Podmiot zabezpieczeń zawiera skojarzony obiekt Tożsamości , który zawiera informacje o użytkowniku. Jeśli użytkownik jest uwierzytelniony, właściwość Identity.IsAuthenticated zwraca wartość true. W przypadku żądań anonimowych funkcja IsAuthenticated zwraca wartość false. Aby uzyskać więcej informacji na temat podmiotów zabezpieczeń, zobacz Zabezpieczenia oparte na rolach.

Programy obsługi komunikatów HTTP na potrzeby uwierzytelniania

Zamiast używać hosta do uwierzytelniania, można umieścić logikę uwierzytelniania w procedurze obsługi komunikatów HTTP. W takim przypadku program obsługi komunikatów sprawdza żądanie HTTP i ustawia jednostkę.

Kiedy należy używać procedur obsługi komunikatów do uwierzytelniania? Oto kilka kompromisów:

  • Moduł HTTP widzi wszystkie żądania przechodzące przez potok ASP.NET. Procedura obsługi komunikatów widzi tylko żądania kierowane do internetowego interfejsu API.
  • Można ustawić procedury obsługi komunikatów na trasę, co umożliwia zastosowanie schematu uwierzytelniania do określonej trasy.
  • Moduły HTTP są specyficzne dla usług IIS. Programy obsługi komunikatów są niezależne od hosta, dzięki czemu mogą być używane zarówno z hostingiem internetowym, jak i własnym hostingiem.
  • Moduły HTTP uczestniczą w rejestrowaniu, inspekcji i tak dalej w usługach IIS.
  • Moduły HTTP działają wcześniej w potoku. Jeśli obsługujesz uwierzytelnianie w procedurze obsługi komunikatów, podmiot zabezpieczeń nie zostanie ustawiony do momentu uruchomienia programu obsługi. Ponadto podmiot zabezpieczeń powraca do poprzedniego podmiotu zabezpieczeń, gdy odpowiedź opuszcza procedurę obsługi komunikatów.

Ogólnie rzecz biorąc, jeśli nie musisz obsługiwać samoobsługowego hostingu, moduł HTTP jest lepszym rozwiązaniem. Jeśli potrzebujesz obsługi samoobsługowego hostingu, rozważ procedurę obsługi komunikatów.

Ustawianie podmiotu zabezpieczeń

Jeśli aplikacja wykonuje dowolną niestandardową logikę uwierzytelniania, musisz ustawić jednostkę w dwóch miejscach:

  • Thread.CurrentPrincipal. Ta właściwość jest standardowym sposobem ustawiania podmiotu zabezpieczeń wątku na platformie .NET.
  • HttpContext.Current.User. Ta właściwość jest specyficzna dla ASP.NET.

Poniższy kod pokazuje, jak ustawić jednostkę:

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

W przypadku hostingu internetowego należy ustawić podmiot zabezpieczeń w obu miejscach; w przeciwnym razie kontekst zabezpieczeń może stać się niespójny. Jednak w przypadku samodzielnego hostowania wartość HttpContext.Current ma wartość null. Aby upewnić się, że kod jest niezależny od hosta, sprawdź wartość null przed przypisaniem do obiektu HttpContext.Current, jak pokazano.

Autoryzacja

Autoryzacja odbywa się później w potoku, bliżej kontrolera. Dzięki temu można dokonać bardziej szczegółowych wyborów podczas udzielania dostępu do zasobów.

  • Filtry autoryzacji są uruchamiane przed akcją kontrolera. Jeśli żądanie nie jest autoryzowane, filtr zwraca odpowiedź o błędzie, a akcja nie jest wywoływana.
  • W ramach akcji kontrolera można pobrać bieżącą jednostkę z właściwości ApiController.User . Na przykład można filtrować listę zasobów na podstawie nazwy użytkownika, zwracając tylko te zasoby, które należą do tego użytkownika.

Diagram potoku uwierzytelniania i autoryzacji.

Używanie atrybutu [Autoryzuj]

Internetowy interfejs API udostępnia wbudowany filtr autoryzacji AuthorizeAttribute. Ten filtr sprawdza, czy użytkownik jest uwierzytelniony. Jeśli nie, zwraca kod stanu HTTP 401 (Brak autoryzacji), bez wywoływania akcji.

Filtr można zastosować globalnie na poziomie kontrolera lub na poziomie poszczególnych akcji.

Globalnie: aby ograniczyć dostęp dla każdego kontrolera internetowego interfejsu API, dodaj filtr AuthorizeAttribute do listy filtrów globalnych:

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

Kontroler: Aby ograniczyć dostęp do określonego kontrolera, dodaj filtr jako atrybut do kontrolera:

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

Akcja: Aby ograniczyć dostęp do określonych akcji, dodaj atrybut do metody akcji:

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

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

Alternatywnie można ograniczyć kontroler, a następnie zezwolić na anonimowy dostęp do określonych akcji przy użyciu atrybutu [AllowAnonymous] . W poniższym przykładzie metoda jest ograniczona, Post ale Get metoda zezwala na dostęp anonimowy.

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

    public HttpResponseMessage Post() { ... }
}

W poprzednich przykładach filtr umożliwia każdemu uwierzytelnionemu użytkownikowi dostęp do metod ograniczonych; są trzymani tylko anonimowi użytkownicy. Możesz również ograniczyć dostęp do określonych użytkowników lub użytkowników w określonych rolach:

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

Uwaga

Filtr AuthorizeAttribute dla kontrolerów interfejsu API sieci Web znajduje się w przestrzeni nazw System.Web.Http . Istnieje podobny filtr dla kontrolerów MVC w przestrzeni nazw System.Web.Mvc , która nie jest zgodna z kontrolerami internetowego interfejsu API.

Niestandardowe filtry autoryzacji

Aby napisać niestandardowy filtr autoryzacji, pochodzi z jednego z następujących typów:

  • AuthorizeAttribute. Rozszerz tę klasę, aby wykonać logikę autoryzacji na podstawie bieżącego użytkownika i ról użytkownika.
  • AuthorizationFilterAttribute. Rozszerz tę klasę, aby wykonywać synchroniczną logikę autoryzacji, która nie musi być oparta na bieżącym użytkowniku lub roli.
  • IAuthorizationFilter. Zaimplementuj ten interfejs, aby wykonać logikę autoryzacji asynchronicznej; jeśli na przykład logika autoryzacji wykonuje asynchroniczne wywołania we/wy lub sieciowe. (Jeśli logika autoryzacji jest powiązana z procesorem CPU, łatwiej jest pochodzić z atrybutu AuthorizationFilterAttribute, ponieważ nie trzeba pisać metody asynchronicznej).

Na poniższym diagramie przedstawiono hierarchię klas dla klasy AuthorizeAttribute .

Diagram hierarchii klas dla klasy Authorize Attribute.

Diagram hierarchii klas dla klasy Authorize Attribute. Autoryzowanie atrybutu znajduje się u dołu z strzałką wskazującą atrybut filtru autoryzacji i strzałką wskazującą filtr autoryzacji w górę u góry.

Autoryzacja wewnątrz akcji kontrolera

W niektórych przypadkach możesz zezwolić na kontynuowanie żądania, ale zmienić zachowanie na podstawie podmiotu zabezpieczeń. Na przykład zwracane informacje mogą ulec zmianie w zależności od roli użytkownika. W ramach metody kontrolera można pobrać bieżącą jednostkę z właściwości ApiController.User .

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