true password net impersonate domain asp c# asp.net-web-api impersonation windows-security

c# - password - impersonate asp net



¿Cómo hacer que HttpClient pase credenciales junto con la solicitud? (7)

En .NET Core, logré obtener un System.Net.Http.HttpClient con UseDefaultCredentials = true para pasar las credenciales de Windows del usuario autenticado a un servicio de fondo utilizando WindowsIdentity.RunImpersonated .

HttpClient client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true } ); HttpResponseMessage response = null; if (identity is WindowsIdentity windowsIdentity) { await WindowsIdentity.RunImpersonated(windowsIdentity.AccessToken, async () => { var request = new HttpRequestMessage(HttpMethod.Get, url) response = await client.SendAsync(request); }); }

Tengo una aplicación web (alojada en IIS) que habla con un servicio de Windows. El servicio de Windows está utilizando ASP.Net MVC Web API (autohospedado), por lo que se puede comunicar a través de http utilizando JSON. La aplicación web está configurada para hacer suplantación, la idea es que el usuario que realiza la solicitud a la aplicación web debe ser el usuario que la aplicación web utiliza para realizar la solicitud al servicio. La estructura se ve así:

(El usuario resaltado en rojo es el usuario al que se hace referencia en los ejemplos a continuación).

La aplicación web realiza solicitudes al servicio de Windows utilizando un HttpClient :

var httpClient = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true }); httpClient.GetStringAsync("http://localhost/some/endpoint/");

Esto realiza la solicitud al servicio de Windows, pero no pasa correctamente las credenciales (el servicio informa al usuario como IIS APPPOOL/ASP.NET 4.0 ). Esto no es lo que quiero que pase .

Si cambio el código anterior para usar un WebClient , las credenciales del usuario se pasan correctamente:

WebClient c = new WebClient { UseDefaultCredentials = true }; c.DownloadStringAsync(new Uri("http://localhost/some/endpoint/"));

Con el código anterior, el servicio informa al usuario como el usuario que realizó la solicitud a la aplicación web.

¿Qué estoy haciendo mal con la implementación de HttpClient que está causando que no pase las credenciales correctamente (o es un error con el HttpClient )?

El motivo por el que quiero usar HttpClient es que tiene una API asíncrona que funciona bien con Task , mientras que la API asyc del WebClient debe manejarse con eventos.


Lo que intenta hacer es conseguir que NTLM reenvíe la identidad al siguiente servidor, lo cual no puede hacer; solo puede hacer una suplantación que solo le da acceso a los recursos locales. No le permitirá cruzar un límite de máquina. La autenticación Kerberos admite la delegación (lo que necesita) mediante el uso de tickets, y el ticket se puede reenviar cuando todos los servidores y aplicaciones de la cadena se configuran correctamente y Kerberos se configura correctamente en el dominio. Entonces, en resumen, debe cambiar de usar NTLM a Kerberos.

Para obtener más información sobre las opciones de autenticación de Windows disponibles para usted y cómo funcionan, comience en: http://msdn.microsoft.com/en-us/library/ff647076.aspx


Me funcionó después de configurar un usuario con acceso a Internet en el servicio de Windows.

En mi código:

HttpClientHandler handler = new HttpClientHandler(); handler.Proxy = System.Net.WebRequest.DefaultWebProxy; handler.Proxy.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials; ..... HttpClient httpClient = new HttpClient(handler) ....


OK, así que gracias a todos los contribuyentes anteriores. Estoy usando .NET 4.6 y también tuvimos el mismo problema. Pasé tiempo depurando System.Net.Http, específicamente HttpClientHandler, y encontré lo siguiente:

if (ExecutionContext.IsFlowSuppressed()) { IWebProxy webProxy = (IWebProxy) null; if (this.useProxy) webProxy = this.proxy ?? WebRequest.DefaultWebProxy; if (this.UseDefaultCredentials || this.Credentials != null || webProxy != null && webProxy.Credentials != null) this.SafeCaptureIdenity(state); }

Entonces, después de evaluar que ExecutionContext.IsFlowSuppressed () podría haber sido el culpable, envolví nuestro código de Suplantación de la siguiente manera:

using (((WindowsIdentity)ExecutionContext.Current.Identity).Impersonate()) using (System.Threading.ExecutionContext.SuppressFlow()) { // HttpClient code goes here! }

El código dentro de SafeCaptureIdenity (no es mi error ortográfico), toma WindowsIdentity.Current () que es nuestra identidad suplantada. Esto se está recogiendo porque ahora estamos suprimiendo el flujo. Debido al uso / eliminación, se reinicia después de la invocación.

¡Ahora parece funcionar para nosotros, phew!


Ok, tomé el código de Joshoun y lo hice genérico. No estoy seguro de si debería implementar el patrón singleton en la clase SynchronousPost. Tal vez alguien más conocedor pueda ayudar.

Implementación

// Supongo que tienes tu propio tipo concreto. En mi caso, estoy usando el código primero con una clase llamada FileCategory

FileCategory x = new FileCategory { CategoryName = "Some Bs"}; SynchronousPost<FileCategory>test= new SynchronousPost<FileCategory>(); test.PostEntity(x, "/api/ApiFileCategories");

Clase genérica aquí. Puedes pasar cualquier tipo

public class SynchronousPost<T>where T :class { public SynchronousPost() { Client = new WebClient { UseDefaultCredentials = true }; } public void PostEntity(T PostThis,string ApiControllerName)//The ApiController name should be "/api/MyName/" { //this just determines the root url. Client.BaseAddress = string.Format( ( System.Web.HttpContext.Current.Request.Url.Port != 80) ? "{0}://{1}:{2}" : "{0}://{1}", System.Web.HttpContext.Current.Request.Url.Scheme, System.Web.HttpContext.Current.Request.Url.Host, System.Web.HttpContext.Current.Request.Url.Port ); Client.Headers.Add(HttpRequestHeader.ContentType, "application/json;charset=utf-8"); Client.UploadData( ApiControllerName, "Post", Encoding.UTF8.GetBytes ( JsonConvert.SerializeObject(PostThis) ) ); } private WebClient Client { get; set; } }

Mis clases Api se ven así, si eres curioso

public class ApiFileCategoriesController : ApiBaseController { public ApiFileCategoriesController(IMshIntranetUnitOfWork unitOfWork) { UnitOfWork = unitOfWork; } public IEnumerable<FileCategory> GetFiles() { return UnitOfWork.FileCategories.GetAll().OrderBy(x=>x.CategoryName); } public FileCategory GetFile(int id) { return UnitOfWork.FileCategories.GetById(id); } //Post api/ApileFileCategories public HttpResponseMessage Post(FileCategory fileCategory) { UnitOfWork.FileCategories.Add(fileCategory); UnitOfWork.Commit(); return new HttpResponseMessage(); } }

Estoy usando ninject y repo pattern con la unidad de trabajo. De todos modos, la clase genérica anterior realmente ayuda.


Puede configurar HttpClient para que pase automáticamente credenciales como esta:

myClient = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true })


También estaba teniendo este mismo problema. Desarrollé una solución sincrónica gracias a la investigación realizada por @tpeczek en el siguiente artículo de SO: No se puede autenticar en el servicio API web de ASP.NET con HttpClient.

Mi solución utiliza un WebClient , que, como señaló correctamente, pasa las credenciales sin problema. La razón por la que HttpClient no funciona es porque la seguridad de Windows deshabilita la capacidad de crear nuevos subprocesos bajo una cuenta suplantada (ver el artículo SO anterior). HttpClient crea nuevos subprocesos a través de la Fábrica de Tareas causando el error. WebClient por otro lado, se ejecuta de forma síncrona en el mismo subproceso evitando así la regla y reenviando sus credenciales.

Aunque el código funciona, la desventaja es que no funcionará de manera asíncrona.

var wi = (System.Security.Principal.WindowsIdentity)HttpContext.Current.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(); }

Nota: Requiere paquete NuGet: Newtonsoft.Json, que es el mismo serializador JSON que utiliza WebAPI.