iis-7.5 - instalar wcf en iis
No se puede autenticar en el servicio Web Api de ASP.NET con HttpClient (4)
Tengo un servicio ASP.NET Web API que se ejecuta en un servidor web con la Autenticación de Windows habilitada.
Tengo un sitio de cliente creado en MVC4 que se ejecuta en un sitio diferente en el mismo servidor web que utiliza HttpClient para extraer datos del servicio. Este sitio de cliente se ejecuta con suplantación de identidad habilitada y también usa la autenticación de Windows.
El servidor web es Windows Server 2008 R2 con IIS 7.5.
El desafío que tengo es conseguir que HttpClient pase al usuario actual de Windows como parte de su proceso de autenticación. He configurado el HttpClient de esta manera:
var clientHandler = new HttpClientHandler();
clientHandler.UseDefaultCredentials = true;
clientHandler.PreAuthenticate = true;
clientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
var httpClient = new HttpClient(clientHandler);
Según entiendo, si se ejecuta el sitio con suplantación de identidad habilitada y luego se construye el cliente de esta manera, el cliente se autenticará utilizando la identidad suplantada del usuario que ha iniciado sesión actualmente.
Esto no está ocurriendo. De hecho, el cliente no parece autenticarse en absoluto.
El servicio está configurado para usar la autenticación de Windows y esto parece funcionar perfectamente. Puedo ir a http://server/api/shippers en mi navegador web y recibir una solicitud de autenticación de Windows. Una vez ingresado, recibo los datos solicitados.
En los registros de IIS, veo que se reciben las solicitudes de API sin autenticación y se recibe una respuesta de desafío 401.
La documentación sobre este parece ser escasa.
Necesito una idea de lo que podría estar mal u otra forma de usar la autenticación de Windows con esta aplicación.
Gracias, Craig
Investigué el código fuente de HttpClientHandler (la última versión que pude obtener) y esto es lo que se puede encontrar en el método 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);
Ahora, si comprueba dentro de su código el valor de SecurityContext.IsWindowsIdentityFlowSuppressed (), probablemente sea cierto. Como resultado, el método StartRequest se ejecuta en un nuevo hilo con las credenciales del proceso asp.net (no las credenciales del usuario suplantado).
Hay dos formas posibles de salir de esto. Si tiene acceso al servidor suyo aspnet_config.config, debe establecer las siguientes configuraciones (configurarlas en web.config parece no tener ningún efecto):
<legacyImpersonationPolicy enabled="false"/>
<alwaysFlowImpersonationPolicy enabled="true"/>
Si no puede cambiar el archivo aspnet_config.config, deberá crear su propio HttpClientHandler para admitir este escenario.
ACTUALIZACIÓN CON RESPECTO AL USO DE FQDN
El problema que ha afectado aquí es una función en Windows que está diseñada para proteger contra los "ataques de reflexión". Para solucionar este problema, debe incluir en la lista blanca el dominio al que intenta acceder en la máquina que intenta acceder al servidor. Siga los pasos a continuación:
- Vaya a Inicio -> Ejecutar -> regedit
- Ubique la clave de registro
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Lsa/MSV1_0
. - Haga clic con el botón derecho en él, elija Nuevo y luego Valor de cadena múltiple .
- Escriba
BackConnectionHostNames
( ENTER ). - Haga clic con el botón derecho sobre el valor creado y elija Modificar .
- Ponga los nombres de host para los sitios que están en la computadora local en el cuadro de valor y haga clic en Aceptar (cada nombre de host / FQDN debe estar en su propia línea, sin comodines, el nombre debe coincidir exactamente) .
- Guarde todo y reinicie la máquina
Puede leer el artículo completo de KB sobre el tema here .
La razón por la que esto no funciona es porque necesita autenticación de doble salto.
El primer salto es el servidor web, obtener la suplantación con la autenticación de Windows para funcionar, no hay problema. Pero al usar HttpClient o WebClient para autenticarse en otro servidor, el servidor web debe ejecutarse en una cuenta que tenga permiso para realizar la delegación necesaria.
Vea lo siguiente para más detalles:
http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx
Se soluciona usando el comando "setspn":
http://www.phishthis.com/2009/10/24/how-to-configure-ad-sql-and-iis-for-two-hop-kerberos-authentication-2/ (Necesitará suficientes derechos de acceso a realizar estas operaciones)
Simplemente considere lo que sucedería si se permite a cualquier servidor reenviar sus credenciales como lo desee ... Para evitar este problema de seguridad, el controlador de dominio necesita saber qué cuentas tienen permiso para realizar la delegación.
Para suplantar al usuario original (autenticado), use la siguiente configuración en el archivo Web.config:
<authentication mode="Windows" />
<identity impersonate="true" />
Con esta configuración, ASP.NET siempre se hace pasar por el usuario autenticado y todos los recursos se realizan utilizando el contexto de seguridad del usuario autenticado.
También estaba teniendo este mismo problema. Gracias a la investigación realizada por @tpeczek, desarrollé la siguiente solución: en lugar de usar HttpClient (que crea subprocesos y envía solicitudes asincrónicas), utilicé la clase WebClient que emite solicitudes en el mismo hilo. Hacerlo me permite pasar la identidad del usuario a WebAPI desde otra aplicación ASP.NET.
El inconveniente obvio es que esto no funcionará de manera asincrónica.
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();
}
Nota: Requiere paquete NuGet: Newtonsoft.Json, que es el mismo serializador JSON que utiliza WebAPI.