Authentication and Authorization in ASP.NET Web API (Authentification et autorisation dans l’API Web ASP.NET)
par Rick Anderson
Vous avez créé une API web, mais vous souhaitez maintenant contrôler l’accès à celui-ci. Dans cette série d’articles, nous allons examiner certaines options pour sécuriser une API web à partir d’utilisateurs non autorisés. Cette série couvre à la fois l’authentification et l’autorisation.
- L’authentification connaît l’identité de l’utilisateur. Par exemple, Alice se connecte avec son nom d’utilisateur et son mot de passe, et le serveur utilise le mot de passe pour authentifier Alice.
- L’autorisation détermine si un utilisateur est autorisé à effectuer une action. Par exemple, Alice a l’autorisation d’obtenir une ressource, mais pas de créer une ressource.
Le premier article de la série donne une vue d’ensemble générale de l’authentification et de l’autorisation dans API Web ASP.NET. D’autres rubriques décrivent des scénarios d’authentification courants pour l’API web.
Notes
Merci aux gens qui ont examiné cette série et ont fourni des commentaires précieux : Rick Anderson, Levi Broderick, Barry Dorrans, Tom Dykstra, Hongmei Ge, David Matson, Daniel Roth, Tim Teebken.
Authentification
L’API web suppose que l’authentification se produit dans l’hôte. Pour l’hébergement web, l’hôte est IIS, qui utilise des modules HTTP pour l’authentification. Vous pouvez configurer votre projet pour utiliser l’un des modules d’authentification intégrés à IIS ou ASP.NET, ou écrire votre propre module HTTP pour effectuer une authentification personnalisée.
Lorsque l’hôte authentifie l’utilisateur, il crée un principal, qui est un objet IPrincipal qui représente le contexte de sécurité sous lequel le code est en cours d’exécution. L’hôte attache le principal au thread actuel en définissant Thread.CurrentPrincipal. Le principal contient un objet Identity associé qui contient des informations sur l’utilisateur. Si l’utilisateur est authentifié, la propriété Identity.IsAuthenticated retourne true. Pour les requêtes anonymes, IsAuthenticated retourne false. Pour plus d’informations sur les principaux, consultez Sécurité basée sur les rôles.
Gestionnaires de messages HTTP pour l’authentification
Au lieu d’utiliser l’hôte pour l’authentification, vous pouvez placer la logique d’authentification dans un gestionnaire de messages HTTP. Dans ce cas, le gestionnaire de messages examine la requête HTTP et définit le principal.
Quand devez-vous utiliser des gestionnaires de messages pour l’authentification ? Voici quelques compromis :
- Un module HTTP affiche toutes les requêtes qui transitent par le pipeline ASP.NET. Un gestionnaire de messages ne voit que les demandes acheminées vers l’API Web.
- Vous pouvez définir des gestionnaires de messages par itinéraire, ce qui vous permet d’appliquer un schéma d’authentification à un itinéraire spécifique.
- Les modules HTTP sont spécifiques à IIS. Les gestionnaires de messages sont agnostiques et peuvent donc être utilisés avec l’hébergement web et l’auto-hébergement.
- Les modules HTTP participent à la journalisation IIS, à l’audit, et ainsi de suite.
- Les modules HTTP s’exécutent précédemment dans le pipeline. Si vous gérez l’authentification dans un gestionnaire de messages, le principal n’est pas défini tant que le gestionnaire n’est pas exécuté. En outre, le principal revient au principal précédent lorsque la réponse quitte le gestionnaire de messages.
En règle générale, si vous n’avez pas besoin de prendre en charge l’auto-hébergement, un module HTTP est une meilleure option. Si vous devez prendre en charge l’auto-hébergement, envisagez un gestionnaire de messages.
Définition du principal
Si votre application effectue une logique d’authentification personnalisée, vous devez définir le principal sur deux emplacements :
- Thread.CurrentPrincipal. Cette propriété est la méthode standard pour définir le principal du thread dans .NET.
- HttpContext.Current.User. Cette propriété est spécifique à ASP.NET.
Le code suivant montre comment définir le principal :
private void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
Pour l’hébergement web, vous devez définir le principal dans les deux emplacements ; sinon, le contexte de sécurité peut devenir incohérent. Toutefois, pour l’auto-hébergement, HttpContext.Current est null. Pour vous assurer que votre code est agnostique, vérifiez donc null avant d’attribuer à HttpContext.Current, comme indiqué.
Autorisation
L’autorisation se produit plus loin dans le pipeline, plus près du contrôleur. Cela vous permet de faire des choix plus précis lorsque vous accordez l’accès aux ressources.
- Les filtres d’autorisation s’exécutent avant l’action du contrôleur. Si la demande n’est pas autorisée, le filtre retourne une réponse d’erreur et l’action n’est pas appelée.
- Dans une action de contrôleur, vous pouvez obtenir le principal actuel à partir de la propriété ApiController.User . Par exemple, vous pouvez filtrer une liste de ressources en fonction du nom d’utilisateur, en retournant uniquement ces ressources appartenant à cet utilisateur.
Utilisation de l’attribut [Autoriser]
L’API web fournit un filtre d’autorisation intégré, AuthorizationAttribute. Ce filtre vérifie si l’utilisateur est authentifié. Si ce n’est pas le cas, il retourne le code d’état HTTP 401 (non autorisé), sans appeler l’action.
Vous pouvez appliquer le filtre globalement, au niveau du contrôleur ou au niveau des actions individuelles.
Global : Pour restreindre l’accès à chaque contrôleur d’API web, ajoutez le filtre AuthorizeAttribute à la liste de filtres globale :
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new AuthorizeAttribute());
}
Contrôleur : Pour restreindre l’accès à un contrôleur spécifique, ajoutez le filtre en tant qu’attribut au contrôleur :
// Require authorization for all actions on the controller.
[Authorize]
public class ValuesController : ApiController
{
public HttpResponseMessage Get(int id) { ... }
public HttpResponseMessage Post() { ... }
}
Action : Pour restreindre l’accès pour des actions spécifiques, ajoutez l’attribut à la méthode d’action :
public class ValuesController : ApiController
{
public HttpResponseMessage Get() { ... }
// Require authorization for a specific action.
[Authorize]
public HttpResponseMessage Post() { ... }
}
Vous pouvez également restreindre le contrôleur, puis autoriser l’accès anonyme à des actions spécifiques à l’aide de l’attribut [AllowAnonymous]
. Dans l’exemple suivant, la Post
méthode est restreinte, mais la Get
méthode autorise l’accès anonyme.
[Authorize]
public class ValuesController : ApiController
{
[AllowAnonymous]
public HttpResponseMessage Get() { ... }
public HttpResponseMessage Post() { ... }
}
Dans les exemples précédents, le filtre permet à tout utilisateur authentifié d’accéder aux méthodes restreintes ; seuls les utilisateurs anonymes sont conservés. Vous pouvez également limiter l’accès à des utilisateurs spécifiques ou aux utilisateurs dans des rôles spécifiques :
// Restrict by user:
[Authorize(Users="Alice,Bob")]
public class ValuesController : ApiController
{
}
// Restrict by role:
[Authorize(Roles="Administrators")]
public class ValuesController : ApiController
{
}
Notes
Le filtre AuthorizeAttribute pour les contrôleurs d’API web se trouve dans l’espace de noms System.Web.Http . Il existe un filtre similaire pour les contrôleurs MVC dans l’espace de noms System.Web.Mvc , qui n’est pas compatible avec les contrôleurs d’API Web.
Filtres d’autorisation personnalisés
Pour écrire un filtre d’autorisation personnalisé, dérivez de l’un de ces types :
- AuthorizeAttribute. Étendez cette classe pour effectuer une logique d’autorisation en fonction de l’utilisateur actuel et des rôles de l’utilisateur.
- AuthorizationFilterAttribute. Étendez cette classe pour effectuer une logique d’autorisation synchrone qui n’est pas nécessairement basée sur l’utilisateur ou le rôle actuel.
- IAuthorizationFilter. Implémentez cette interface pour effectuer une logique d’autorisation asynchrone ; par exemple, si votre logique d’autorisation effectue des appels d’E/S asynchrones ou réseau. (Si votre logique d’autorisation est liée au processeur, il est plus simple de dériver de AuthorizationFilterAttribute, car vous n’avez pas besoin d’écrire une méthode asynchrone.)
Le diagramme suivant montre la hiérarchie de classes de la classe AuthorizeAttribute .
Diagramme de la hiérarchie de classes pour la classe Authorize Attribute. Autoriser l’attribut se trouve en bas, avec une flèche pointant vers le haut vers l’attribut de filtre d’autorisation et une flèche pointant vers le haut vers le haut.
Autorisation à l’intérieur d’une action de contrôleur
Dans certains cas, vous pouvez autoriser une demande à continuer, mais modifier le comportement en fonction du principal. Par exemple, les informations que vous retournez peuvent changer en fonction du rôle de l’utilisateur. Dans une méthode de contrôleur, vous pouvez obtenir le principal actuel à partir de la propriété ApiController.User .
public HttpResponseMessage Get()
{
if (User.IsInRole("Administrators"))
{
// ...
}
}