43

Tengo un proyecto ASP.NET MVC 4 usando la API web. En el controlador, he establecido que la clase requiera autorización utilizando el atributo [Autorizar]. Para la autenticación, estoy usando el proveedor de membresía de ASP.NET y tengo configurado mi Web.Config para usar la autenticación de "Formularios". Aquí es donde estoy atascado:Autenticación API web ASP.NET MVC 4 con proveedor de membresía

Todo está funcionando bien hasta el punto en que he terminado de probar la API y quiero asegurar el controlador con el atributo [Autorizar] para poder comenzar a probar la autenticación contra usuarios en mi Proveedor de Membresía. Por lo que el fuego de Fiddler y hacer la misma llamada adición de la Autorización: atributo básico junto con un nombre de usuario: contraseña de mi proveedor de pertenencia de este modo:

enter image description here

La respuesta que recibo es 401 no autorizado y bajo "autenticación" Obtengo "No hay encabezado WWW-Authenticate presente". Entonces me doy cuenta de que la API está buscando una clave codificada SHA1. Por lo que el fuego de un generador SHA1 de una búsqueda y obtener un hash de mi nombre de usuario: contraseña y se actualiza el encabezado de solicitud de esta manera:

enter image description here

esto no funciona bien y me da los mismos resultados. También obviamente necesito algún tipo de "clave secreta compartida" para usar con el servidor para decodificar mi nombre de usuario/contraseña.

Así que mis preguntas:

  1. ¿Cómo consigo esta clave desde el servidor (o en este caso de IIS virtual corriendo VS 2012).
  2. ¿Cómo puedo usar esto para hacer llamadas autenticadas en Fiddler usando nombres de usuario/contraseñas de un proveedor de membresía ASP.NET?
  3. Cómo usar esto en mi aplicación cliente para hacer las mismas llamadas (aplicación C# WPF).
  4. ¿Es esto mejor práctico cuando se combina con SSL en mis llamadas HTTP? Si no es lo que es?

Gracias de antemano!

Respuesta

67

Puede usar basic authentication con SSL. En el lado del servidor que podríamos escribir un controlador delega a medida que verificará las credenciales mediante la consulta del proveedor de memebership que hemos registrado, y si es válido, recuperar los papeles y establecer el director actual:

public class BasicAuthenticationMessageHandler : DelegatingHandler 
{ 
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     var authHeader = request.Headers.Authorization; 

     if (authHeader == null) 
     { 
      return base.SendAsync(request, cancellationToken); 
     } 

     if (authHeader.Scheme != "Basic") 
     { 
      return base.SendAsync(request, cancellationToken); 
     } 

     var encodedUserPass = authHeader.Parameter.Trim(); 
     var userPass = Encoding.ASCII.GetString(Convert.FromBase64String(encodedUserPass)); 
     var parts = userPass.Split(":".ToCharArray()); 
     var username = parts[0]; 
     var password = parts[1]; 

     if (!Membership.ValidateUser(username, password)) 
     { 
      return base.SendAsync(request, cancellationToken); 
     } 

     var identity = new GenericIdentity(username, "Basic"); 
     string[] roles = Roles.Provider.GetRolesForUser(username); 
     var principal = new GenericPrincipal(identity, roles); 
     Thread.CurrentPrincipal = principal; 
     if (HttpContext.Current != null) 
     { 
      HttpContext.Current.User = principal; 
     } 

     return base.SendAsync(request, cancellationToken); 
    } 
} 

A continuación, registrar este controlador en Application_Start:

GlobalConfiguration.Configuration.MessageHandlers.Add(
    new BasicAuthenticationMessageHandler() 
); 

Ahora podríamos tener un controlador de API que se adorna con el [Autorizar] atributo para garantizar que sólo los usuarios autenticados pueden acceder a sus acciones:

[Authorize] 
public class ValuesController : ApiController 
{ 
    public string Get() 
    { 
     return string.Format("Hello {0}", User.Identity.Name); 
    } 
} 

bien, ahora vamos a ver un cliente de ejemplo:

using System; 
using System.Net; 
using System.Net.Http; 
using System.Net.Http.Headers; 
using System.Text; 

class Program 
{ 
    static void Main() 
    { 
     // since for testing purposes I am using IIS Express 
     // with an invalid SSL certificate I need to desactivate 
     // the check for this certificate. 
     ServicePointManager.ServerCertificateValidationCallback += 
      (sender, certificate, chain, sslPolicyErrors) => true; 

     using (var client = new HttpClient()) 
     { 
      var buffer = Encoding.ASCII.GetBytes("john:secret"); 
      var authHeader = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(buffer)); 
      client.DefaultRequestHeaders.Authorization = authHeader; 
      var task = client.GetAsync("https://localhost:44300/api/values"); 
      if (task.Result.StatusCode == HttpStatusCode.Unauthorized) 
      { 
       Console.WriteLine("wrong credentials"); 
      } 
      else 
      { 
       task.Result.EnsureSuccessStatusCode(); 
       Console.WriteLine(task.Result.Content.ReadAsAsync<string>().Result); 
      } 
     } 
    } 
} 
+0

Gracias Darin, esto es precisamente lo que necesitaba! – CodeAbundance

+0

'User.Identity.Name' en el método del controlador está vacío para mí,' User.Identity.IsAuthenticated' es 'false'. Puedo ver en el depurador que 'Thread.CurrentPrincipal' se establece en el principal con el nombre de usuario correcto y las funciones en el manejador de mensajes. También el '[Autorización (Roles = ...) se respeta el atributo '] ': si utilizo un usuario con roles incorrectos, recibo una respuesta no autorizada y no alcanzo el método del controlador. Pero 'User.Identity' no tiene el valor que he establecido en el manejador de mensajes. ¿Tienes una idea de lo que podría estar mal? – Slauma

+0

@Slauma, no puedo reproducir esto. Tal vez podría comenzar un nuevo hilo que explica paso a paso cómo reproducir el problema (a partir de un proyecto vacío, por supuesto). –

Cuestiones relacionadas