visual tutorial studio paso example ejemplo crear asp.net .net asp.net-mvc security asp.net-web-api

asp.net - studio - web api c# tutorial



¿Cómo puedo configurar de manera segura el principal del usuario en un WebAPI HttpMessageHandler personalizado? (3)

Con su MessageHandler personalizado, puede agregar la propiedad MS_UserPrincipal llamando al método de extensión HttpRequestMessageExtensionMethods.SetUserPrincipal definido en System.ServiceModel.Channels :

protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { var user = new GenericPrincipal(new GenericIdentity("UserID"), null); request.SetUserPrincipal(user); return base.SendAsync(request, cancellationToken); }

Tenga en cuenta que esto solo agrega esta propiedad a la colección Propiedades de la Solicitud, no cambia el Usuario adjunto al ApiController.

Para la autenticación básica, he implementado un HttpMessageHandler personalizado basado en el ejemplo que se muestra en la respuesta de Darin Dimitrov aquí: https://stackoverflow.com/a/11536349/270591

El código crea un principal de instancia de tipo GenericPrincipal con nombre de usuario y roles y luego establece este principal para el principal actual del hilo:

Thread.CurrentPrincipal = principal;

Más adelante, en un método ApiController , el principal se puede leer accediendo a los controladores User Propiedad del User :

public class ValuesController : ApiController { public void Post(TestModel model) { var user = User; // this should be the principal set in the handler //... } }

Esto pareció funcionar bien hasta que recientemente agregué un MediaTypeFormatter personalizado que usa la biblioteca de Task manera:

public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { var task = Task.Factory.StartNew(() => { // some formatting happens and finally a TestModel is returned, // simulated here by just an empty model return (object)new TestModel(); }); return task; }

(Tengo este enfoque para comenzar una tarea con Task.Factory.StartNew en ReadFromStreamAsync partir de un código de ejemplo. ¿Es incorrecto y tal vez la única razón del problema?)

Ahora, "algunas veces", y para mí parece ser aleatorio, el principal del User en el método del controlador ya no es el principal que he configurado en MessageHandler, es decir, el nombre de usuario, el indicador Authenticated y los roles se han perdido. La razón parece ser que el MediaTypeFormatter personalizado provoca un cambio del hilo entre MessageHandler y el método del controlador. Lo he confirmado comparando los valores de Thread.CurrentThread.ManagedThreadId en MessageHandler y en el método del controlador. "A veces" son diferentes y luego el director está "perdido".

He buscado ahora una alternativa a la configuración de Thread.CurrentPrincipal para transferir de alguna manera el principal de forma segura desde el MessageHandler personalizado al método del controlador y en este blog se solicitan las propiedades de solicitud:

request.Properties.Add(HttpPropertyKeys.UserPrincipalKey, new GenericPrincipal(identity, new string[0]));

Quería probar eso, pero parece que la clase HttpPropertyKeys (que está en el espacio de nombres System.Web.Http.Hosting ) ya no tiene una propiedad UserPrincipalKey en las versiones recientes de WebApi (versión de lanzamiento y versión final de la semana pasada también) .

Mi pregunta es: ¿cómo puedo cambiar el último fragmento de código anterior para que funcione con la versión actual de WebAPI? O en general: ¿cómo puedo configurar el principal del usuario en un MessageHandler personalizado y acceder de manera confiable en un método de controlador?

Editar

here se menciona que " HttpPropertyKeys.UserPrincipalKey ... se resuelve en “MS_UserPrincipal” ", así que traté de usar:

request.Properties.Add("MS_UserPrincipal", new GenericPrincipal(identity, new string[0]));

Pero no funciona como esperaba: la propiedad ApiController.User no contiene el principal agregado a la colección de Properties anterior.


El problema de perder el principal en un nuevo hilo se menciona aquí:

http://leastprivilege.com/2012/06/25/important-setting-the-client-principal-in-asp-net-web-api/

Importante: configurar el cliente principal en ASP.NET Web API

Debido a algunos mecanismos desafortunados enterrados profundamente en ASP.NET, configurar Thread.CurrentPrincipal en Web API web hosting no es suficiente.

Cuando se aloja en ASP.NET, Thread.CurrentPrincipal puede sobrescribirse con HttpContext.Current.User al crear nuevos hilos. Esto significa que debe establecer el principal tanto en el hilo como en el contexto HTTP.

Y aquí: http://aspnetwebstack.codeplex.com/workitem/264

Hoy, necesitará establecer los dos siguientes para el principal del usuario si usa un manejador de mensajes personalizado para realizar la autenticación en el escenario alojado en la web.

IPrincipal principal = new GenericPrincipal( new GenericIdentity("myuser"), new string[] { "myrole" }); Thread.CurrentPrincipal = principal; HttpContext.Current.User = principal;

He agregado la última línea HttpContext.Current.User = principal (necesita using System.Web; ) al manejador de mensajes y la propiedad User en el ApiController siempre tiene el principal correcto ahora, incluso si el hilo ha cambiado debido a la tarea en MediaTypeFormatter.

Editar

Solo para enfatizarlo: establecer el principal del usuario actual de HttpContext solo es necesario cuando WebApi está alojado en ASP.NET/IIS. Para autohospedarse no es necesario (y no es posible porque HttpContext es una construcción de ASP.NET y no existe cuando se aloja automáticamente).


Para evitar el cambio de contexto, intente utilizar TaskCompletionSource<object> lugar de iniciar manualmente otra tarea en su MediaTypeFormatter personalizado:

public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { var tcs = new TaskCompletionSource<object>(); // some formatting happens and finally a TestModel is returned, // simulated here by just an empty model var testModel = new TestModel(); tcs.SetResult(testModel); return tcs.Task; }