Ověřování a autorizace ve webovém rozhraní API ASP.NET
Rick Anderson
Vytvořili jste webové rozhraní API, ale teď k němu chcete řídit přístup. V této sérii článků se podíváme na některé možnosti zabezpečení webového rozhraní API před neoprávněnými uživateli. Tato série se bude zabývat ověřováním i autorizací.
- Ověřování je znalost identity uživatele. Například Alice se přihlásí pomocí svého uživatelského jména a hesla a server použije heslo k ověření Alice.
- Autorizace určuje, jestli má uživatel povoleno provést akci. Například Alice má oprávnění k získání prostředku, ale ne k vytvoření prostředku.
První článek této série poskytuje obecný přehled ověřování a autorizace ve webovém rozhraní API ASP.NET. Další témata popisují běžné scénáře ověřování pro webové rozhraní API.
Poznámka
Díky lidem, kteří si prostudovali tento seriál a poskytli cennou zpětnou vazbu: Rick Anderson, Levi Broderick, Barry Dorrans, Tom Dykstra, Hongmei Ge, David Matson, Daniel Roth, Tim Teebken.
Authentication
Webové rozhraní API předpokládá, že ověřování probíhá na hostiteli. Pro hostování webu je hostitelem služba IIS, která k ověřování používá moduly HTTP. Projekt můžete nakonfigurovat tak, aby používal kterýkoli z ověřovacích modulů integrovaných ve službě IIS nebo ASP.NET, nebo napsat vlastní modul HTTP pro vlastní ověřování.
Když hostitel ověří uživatele, vytvoří objekt zabezpečení, což je objekt IPrincipal , který představuje kontext zabezpečení, ve kterém je spuštěný kód. Hostitel připojí objekt zabezpečení k aktuálnímu vláknu nastavením Thread.CurrentPrincipal. Objekt zabezpečení obsahuje přidružený objekt identity , který obsahuje informace o uživateli. Pokud je uživatel ověřený, vrátí vlastnost Identity.IsAuthenticatedhodnotu true. V případě anonymních požadavků vrátí IsAuthenticatedhodnotu false. Další informace o objektech zabezpečení najdete v tématu Zabezpečení na základě role.
Obslužné rutiny zpráv HTTP pro ověřování
Místo použití hostitele pro ověřování můžete logiku ověřování vložit do obslužné rutiny zprávy HTTP. V takovém případě obslužná rutina zprávy prozkoumá požadavek HTTP a nastaví objekt zabezpečení.
Kdy byste měli k ověřování použít obslužné rutiny zpráv? Tady je několik kompromisů:
- Modul HTTP vidí všechny požadavky, které procházejí kanálem ASP.NET. Obslužná rutina zprávy vidí jenom požadavky směrované do webového rozhraní API.
- Můžete nastavit obslužné rutiny zpráv pro jednotlivé trasy, které umožňují použít schéma ověřování na konkrétní trasu.
- Moduly HTTP jsou specifické pro službu IIS. Obslužné rutiny zpráv jsou nezávislé na hostitelích, takže je možné je používat jak s hostováním webů, tak s vlastním hostováním.
- Moduly HTTP se účastní protokolování, auditování atd. služby IIS.
- Moduly HTTP se spouštějí dříve v kanálu. Pokud zpracováváte ověřování v obslužné rutině zprávy, objekt zabezpečení se nenastaví, dokud se obslužná rutina nespustí. Objekt zabezpečení se navíc vrátí k předchozímu objektu zabezpečení, když odpověď opustí obslužnou rutinu zprávy.
Obecně platí, že pokud nepotřebujete podporovat samoobslužné hostování, je lepší volbou modul HTTP. Pokud potřebujete podporovat samoobslužné hostování, zvažte obslužnou rutinu zprávy.
Nastavení objektu zabezpečení
Pokud vaše aplikace provádí vlastní logiku ověřování, musíte objekt zabezpečení nastavit na dvou místech:
- Thread.CurrentPrincipal. Tato vlastnost představuje standardní způsob, jak nastavit objekt zabezpečení vlákna v .NET.
- HttpContext.Current.User. Tato vlastnost je specifická pro ASP.NET.
Následující kód ukazuje, jak nastavit objekt zabezpečení:
private void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
Pro hostování webu musíte nastavit objekt zabezpečení na obou místech. jinak může být kontext zabezpečení nekonzistentní. Pro samoobslužné hostování má ale HttpContext.Current hodnotu null. Pokud chcete zajistit, aby váš kód byl nezávislý na hostiteli, zkontrolujte před přiřazením httpContext.Current hodnotu null, jak je znázorněno na obrázku.
Autorizace
Autorizace proběhne později v kanálu, blíže k kontroleru. To vám umožní při udělení přístupu k prostředkům provádět podrobnější volby.
- Filtry autorizace se spouštějí před akcí kontroleru. Pokud požadavek není autorizovaný, vrátí filtr chybovou odpověď a akce se nevyvolá.
- V rámci akce kontroleru můžete získat aktuální objekt zabezpečení z vlastnosti ApiController.User . Můžete například filtrovat seznam prostředků na základě uživatelského jména a vrátit jenom ty prostředky, které patří danému uživateli.
Použití atributu [Authorize]
Webové rozhraní API poskytuje integrovaný filtr autorizace AuthorizeAttribute. Tento filtr kontroluje, jestli je uživatel ověřený. Pokud ne, vrátí stavový kód HTTP 401 (Neautorizováno), aniž by došlo k vyvolání akce.
Filtr můžete použít globálně, na úrovni kontroleru nebo na úrovni jednotlivých akcí.
Globálně: Pokud chcete omezit přístup pro každý kontroler webového rozhraní API, přidejte do globálního seznamu filtrů filtr AuthorizeAttribute :
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new AuthorizeAttribute());
}
Kontroler: Pokud chcete omezit přístup pro konkrétní kontroler, přidejte filtr jako atribut do kontroleru:
// Require authorization for all actions on the controller.
[Authorize]
public class ValuesController : ApiController
{
public HttpResponseMessage Get(int id) { ... }
public HttpResponseMessage Post() { ... }
}
Akce: Pokud chcete omezit přístup pro konkrétní akce, přidejte atribut do metody akce:
public class ValuesController : ApiController
{
public HttpResponseMessage Get() { ... }
// Require authorization for a specific action.
[Authorize]
public HttpResponseMessage Post() { ... }
}
Případně můžete omezit kontroler a pak povolit anonymní přístup ke konkrétním akcím pomocí atributu [AllowAnonymous]
. V následujícím příkladu Post
je metoda omezena, ale metoda Get
umožňuje anonymní přístup.
[Authorize]
public class ValuesController : ApiController
{
[AllowAnonymous]
public HttpResponseMessage Get() { ... }
public HttpResponseMessage Post() { ... }
}
V předchozích příkladech filtr umožňuje všem ověřeným uživatelům přístup k omezeným metodám; jsou mimo přístup pouze anonymní uživatelé. Přístup můžete také omezit na konkrétní uživatele nebo na uživatele s konkrétními rolemi:
// Restrict by user:
[Authorize(Users="Alice,Bob")]
public class ValuesController : ApiController
{
}
// Restrict by role:
[Authorize(Roles="Administrators")]
public class ValuesController : ApiController
{
}
Poznámka
Filtr AuthorizeAttribute pro řadiče webového rozhraní API se nachází v oboru názvů System.Web.Http . Podobný filtr pro řadiče MVC je v oboru názvů System.Web.Mvc , který není kompatibilní s řadiči webového rozhraní API.
Vlastní autorizační filtry
Pokud chcete napsat vlastní autorizační filtr, odvozujte z jednoho z těchto typů:
- AuthorizeAttribute. Rozšiřte tuto třídu tak, aby prováděla autorizační logiku na základě aktuálního uživatele a rolí uživatele.
- AuthorizationFilterAttribute. Rozšiřte tuto třídu tak, aby prováděla synchronní autorizační logiku, která nemusí být nutně založena na aktuálním uživateli nebo roli.
- IAuthorizationFilter. Implementujte toto rozhraní k provádění asynchronní autorizační logiky; Například pokud vaše autorizační logika provádí asynchronní vstupně-výstupní nebo síťová volání. (Pokud je vaše autorizační logika vázaná na procesor, je jednodušší odvodit z atributu AuthorizationFilterAttribute, protože pak nemusíte psát asynchronní metodu.)
Následující diagram znázorňuje hierarchii tříd pro třídu AuthorizeAttribute .
Diagram hierarchie tříd pro třídu Authorize Attribute Atribut Authorize je dole se šipkou, která ukazuje nahoru na atribut filtru autorizace, a šipkou směřující nahoru na I Authorization Filter v horní části.
Autorizace uvnitř akce kontroleru
V některých případech můžete žádosti povolit pokračování, ale změnit chování na základě objektu zabezpečení. Například informace, které vrátíte, se můžou měnit v závislosti na roli uživatele. V rámci metody kontroleru můžete získat aktuální objekt zabezpečení z vlastnosti ApiController.User .
public HttpResponseMessage Get()
{
if (User.IsInRole("Administrators"))
{
// ...
}
}