J'ai du mal à comprendre et à configurer un service et un consommateur où le service s'exécutera en tant qu'utilisateur connecté au consommateur.
Mon consommateur est une application MVC. Mon service est une application Web Api. Les deux fonctionnent sur des serveurs distincts au sein du même domaine. Les deux sont configurés pour utiliser Windows Auth.
Mon code de consommateur est:
private T GenericGet<T>(string p)
{
T result = default(T);
HttpClientHandler handler = new HttpClientHandler() { PreAuthenticate = true, UseDefaultCredentials = true };
using (HttpClient client = new HttpClient(handler))
{
client.BaseAddress = new Uri(serviceEndPoint);
HttpResponseMessage response = client.GetAsync(p).Result;
if (response.IsSuccessStatusCode)
result = response.Content.ReadAsAsync<T>().Result;
}
return result;
}
Dans mon service, j'appelle User.Identity.Name
pour obtenir l'identifiant de l'appelant, mais cet identifiant revient toujours en tant qu'ID de pool d'applications consommateur, et non pas l'utilisateur connecté. Le pool d'applications client s'exécute en tant que service réseau, le serveur lui-même est approuvé pour la délégation. Alors, comment puis-je obtenir l'utilisateur connecté? Code de service:
// GET: /Modules/5/Permissions/
[Authorize]
public ModulePermissionsDTO Get(int ModuleID)
{
Module module= moduleRepository.Find(ModuleID);
if (module== null)
throw new HttpResponseException(HttpStatusCode.NotFound);
// This just shows as the App Pool the MVC consumer is running as (Network Service).
IPrincipal loggedInUser = User;
// Do I need to do something with this instead?
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
ModulePermissionsDTO dto = new ModulePermissionsDTO();
// Construct object here based on User...
return dto;
}
Selon cette question , Kerberos est nécessaire pour que cette configuration fonctionne, car HttpClient s'exécute dans un thread séparé. Cependant, cela me confond parce que je pensais que la demande envoyait un en-tête d'autorisation et que le service devrait donc pouvoir l'utiliser et récupérer le jeton de l'utilisateur. Quoi qu'il en soit, j'ai fait quelques tests avec Kerberos pour vérifier que cela fonctionne correctement sur mon domaine en utilisant la démo de "Situation 5" ici et cela fonctionne mais mes deux applications ne transmettent toujours pas correctement l'utilisateur connecté.
Alors, que dois-je faire pour que cela fonctionne? Kerberos est-il nécessaire ou dois-je effectuer quelque chose dans mon service pour décompresser l'en-tête d'autorisation et créer un objet principal à partir du jeton? Tous les conseils appréciés.
La clé consiste à laisser votre application MVC (consommateur) emprunter l'identité de l'utilisateur appelant, puis à émettre les demandes HTTP de manière synchrone (c'est-à-dire sans générer de nouveau thread). Vous ne devriez pas avoir à vous préoccuper des détails d'implémentation de bas niveau, tels que NTLM vs Kerberos.
Configurez votre application MVC comme suit:
<authentication mode="Windows" />
Pour émettre la requête HTTP, je vous recommande d'utiliser l'excellente bibliothèque RestSharp . Exemple:
var client = new RestClient("<your base url here>");
client.Authenticator = new NtlmAuthenticator();
var request = new RestRequest("Modules/5/Permissions", Method.GET);
var response = client.Execute<ModulePermissionsDTO>(request);
Configurez votre service API Web comme suit:
<authentication mode="Windows" />
Je constate que vous avez déjà décoré votre méthode avec un attribut [Authorize]
qui devrait déclencher un défi d'authentification (HTTP 401) lors de l'accès à la méthode. Vous devriez maintenant pouvoir accéder à l'identité de votre utilisateur final via la propriété User.Identity
de votre classe ApiController.
Le problème clé du double saut est la délégation des informations d'identification de l'utilisateur au deuxième appel. Je veux élaborer un peu à ce sujet. C1 = navigateur client, S1 = premier serveur, S2 = deuxième serveur.
Supposons notre authentification complète de la fenêtre de support système. Lorsque l'utilisateur accède à S1 à partir du navigateur, ses informations d'identification de fenêtre par défaut sont transmises au serveur S1, mais lorsque S1 appelle la base de données S2, il ne transmet pas par défaut les informations d'identification à S2.
Résolution:
Vous trouverez des détails utiles aux liens ci-dessous: Http://blogs.msdn.com/b/farukcelik/archive/2008/01/02/how-to-set-up-a-kerberos-authentication -scenario-with-sql-server-linked-servers.aspx
https://sqlbadboy.wordpress.com/2013/10/11/the-kerberos-double-hop-problem/
Si vous essayez d'accéder à un service hébergé sur une authentification Windows, procédez comme suit.
var request = new RestRequest(Method.POST);
Si vous souhaitez utiliser les informations d'identification par défaut des applications qui doivent avoir un accès sur le serveur de service hébergé
request.UseDefaultCredentials = true;
ou utilisateur ci-dessous pour transmettre les informations d'identification manuellement
request.Credentials = new NetworkCredential("Username", "Password", "Domain");