J'ai un service API Web ASP.NET qui s'exécute sur un serveur Web avec l'authentification Windows activée.
J'ai un site client construit sur MVC4 qui s'exécute sur un site différent, sur le même serveur Web, qui utilise HttpClient pour extraire des données du service. Ce site client fonctionne avec l’emprunt d’identité activé et utilise également l’authentification Windows.
Le serveur Web est Windows Server 2008 R2 avec IIS 7.5.
Le défi que je rencontre est de faire en sorte que HttpClient transmette l’utilisateur Windows actuel dans le cadre de son processus d’authentification. J'ai configuré le HttpClient de cette manière:
var clientHandler = new HttpClientHandler();
clientHandler.UseDefaultCredentials = true;
clientHandler.PreAuthenticate = true;
clientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
var httpClient = new HttpClient(clientHandler);
Je crois comprendre que l’exécution du site avec l’emprunt d’identité activé, puis la construction du client de cette manière, devraient s’authentifier auprès du service en utilisant l’identité empruntée de l’utilisateur actuellement connecté.
Cela n'arrive pas. En fait, le client ne semble pas du tout s’authentifier.
Le service est configuré pour utiliser l'authentification Windows et cela semble fonctionner parfaitement. Je peux accéder à http: // serveur/api/expéditeurs dans mon navigateur Web et être invité à s'authentifier par Windows, une fois entré, je reçois les données demandées.
Dans les journaux IIS, je constate que les demandes d'API sont reçues sans authentification et reçoivent une réponse 401.
La documentation sur celui-ci semble être rare.
J'ai besoin d'un aperçu de ce qui pourrait être faux ou d'une autre façon d'utiliser l'authentification Windows avec cette application.
Merci, Craig
J'ai étudié le code source de HttpClientHandler (la dernière version sur laquelle j'ai pu mettre la main) et voici ce que l'on peut trouver dans la méthode SendAsync:
// BeginGetResponse/BeginGetRequestStream have a lot of setup work to do before becoming async
// (proxy, dns, connection pooling, etc). Run these on a separate thread.
// Do not provide a cancellation token; if this helper task could be canceled before starting then
// nobody would complete the tcs.
Task.Factory.StartNew(startRequest, state);
Maintenant, si vous vérifiez dans votre code la valeur de SecurityContext.IsWindowsIdentityFlowSuppressed (), vous aurez probablement la valeur true. En conséquence, la méthode StartRequest est exécutée dans un nouveau thread avec les informations d'identification du processus asp.net (et non les informations d'identification de l'utilisateur emprunté par l'identité).
Il y a deux manières possibles de s'en sortir. Si vous avez accès à votre serveur aspnet_config.config, vous devez définir les paramètres suivants (ceux de web.config semblent n'avoir aucun effet):
<legacyImpersonationPolicy enabled="false"/>
<alwaysFlowImpersonationPolicy enabled="true"/>
Si vous ne pouvez pas modifier le fichier aspnet_config.config, vous devrez créer votre propre gestionnaire HttpClientHandler pour prendre en charge ce scénario.
MISE À JOUR CONCERNANT L'UTILISATION DU FQDN
Le problème que vous avez rencontré ici est une fonctionnalité de Windows conçue pour protéger contre les "attaques par réflexion". Pour contourner ce problème, vous devez répertorier le domaine auquel vous essayez d'accéder sur la machine qui tente d'accéder au serveur. Suivez les étapes ci-dessous:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0
.BackConnectionHostNames
(ENTER).Vous pouvez lire l'article complet de la base de connaissances concernant le problème ici .
J'avais aussi ce même problème. Grâce aux recherches effectuées par @tpeczek, j'ai développé la solution suivante: au lieu d'utiliser HttpClient (qui crée des threads et envoie des requêtes en mode asynchrone), j'ai utilisé la classe WebClient qui envoie des requêtes sur le même thread. Cela me permet de transmettre l'identité de l'utilisateur à WebAPI à partir d'une autre application ASP.NET.
L'inconvénient évident est que cela ne fonctionnera pas de manière asynchrone.
var wi = (WindowsIdentity)HttpContext.User.Identity;
var wic = wi.Impersonate();
try
{
var data = JsonConvert.SerializeObject(new
{
Property1 = 1,
Property2 = "blah"
});
using (var client = new WebClient { UseDefaultCredentials = true })
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
}
}
catch (Exception exc)
{
// handle exception
}
finally
{
wic.Undo();
}
Remarque: Nécessite le package NuGet: Newtonsoft.Json, identique au sérialiseur JSON utilisé par WebAPI.
La raison pour laquelle cela ne fonctionne pas, c'est parce que vous avez besoin d'une authentification à double saut.
Le premier saut est le serveur Web, l'emprunt d'identité avec l'authentification Windows ne fonctionne pas. Cependant, lorsque vous utilisez HttpClient ou WebClient pour vous authentifier sur un autre serveur, le serveur Web doit être exécuté sur un compte autorisé à effectuer la délégation nécessaire.
Voir ci-dessous pour plus de détails:
http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx
Correction en utilisant la commande "setspn":
http://www.phishthis.com/2009/10/24/how-to-configure-ad-sql-and-iis-for-two-hop-kerberos-authentication-2/ (Vous aurez besoin de droits d'accès suffisants pour effectuer ces opérations.)
Pensez simplement à ce qui se produirait si un serveur était autorisé à transférer vos informations d'identification à votre guise ... Pour éviter ce problème de sécurité, le contrôleur de domaine doit savoir quels comptes sont autorisés à effectuer la délégation.
Pour emprunter l'identité de l'utilisateur d'origine (authentifié), utilisez la configuration suivante dans le fichier Web.config:
<authentication mode="Windows" />
<identity impersonate="true" />
Avec cette configuration, ASP.NET emprunte toujours l'identité de l'utilisateur authentifié et tous les accès aux ressources sont effectués à l'aide du contexte de sécurité de l'utilisateur authentifié.