2011-05-01 11 views
8

Tengo una aplicación MVC3 y las acciones de mi controlador están aseguradas mediante el atributo [Autorizar]. Hasta ahora, tan bueno, auth de formularios funciona muy bien. Ahora quiero agregar una API JSON a mi aplicación para que algunas acciones sean accesibles para clientes que no sean del navegador.Protección de la acción del controlador ASP.NET MVC que devuelve JSON

Tengo problemas para encontrar el diseño "correcto".

1) Cada usuario tiene una clave de API secreta.

2) ID de usuario 5 llamadas http://myapp.com/foocontroller/baraction/5?param1=value1&param2=value2&secure_hash=someValue. Aquí, secure_hash es simplemente el hash SHA1 de los parámetros param1 y param2 anexados con la clave secreta API para el usuario

2)/foocontroller/baraction se decorará con [CustomAuthorize]. Esta será una implementación de AuthorizeAttribute que verificará si la solicitud entra como JSON. Si es así, comprobará el hash y verá si coincide. De lo contrario, si la solicitud es HTML, llamo a la autorización existente.

No estoy del todo seguro de si esto funcionará. ¿Es normal pasar un hash seguro en la cadena de consulta o debería pasarlo como un encabezado HTTP? ¿Es mejor usar la autenticación básica de HTTP en lugar de un hash creado con la clave de API secreta?

¡Las sugerencias de cualquier persona que haya hecho una API web usando ASP.NET MVC serían bienvenidas!

Respuesta

11

Paso la clave secreta de la API junto con el nombre de usuario y la contraseña en el cuerpo de la solicitud. Una vez autorizado, se genera un token y el cliente debe pasarlo en el encabezado Authorization. Esto se verifica en el controlador base en cada solicitud.

  1. Llamadas de cliente myapp.com/authorize que devuelven token de autenticación.
  2. El cliente almacena auth token localmente.
  3. El cliente llama a myapp.com/anycontroller, con authtoken en el encabezado Authorization.

AuthorizeController hereda del controlador. Anycontroller hereda de un controlador base personalizado que realiza el código de autorización.

Mi ejemplo requiere la siguiente ruta que dirige las solicitudes POST a una publicación con nombre de ActionResult en cualquier controlador. Estoy escribiendo esto a mano para simplificarlo tanto como sea posible para darte una idea general. No hay que esperar para cortar y pegar y hacer que funcione :)

routes.MapRoute(
    "post-object", 
    "{controller}", 
    new { controller = "Home", action = "post" {, 
    new { httpMethod = new HttpMethodConstraint("POST")} 
); 

Su controlador de autenticación puede utilizar este

public class AuthorizationController : Controller 
{ 
    public ActionResult Post() 
    { 
     string authBody; 
     var request = ControllerContext.HttpContext.Request; 
     var response = ControllerContext.HttpContext.Response; 

     using(var reader = new StreamReader(request.InputStream)) 
      authBody = reader.ReadToEnd(); 

     // authorize based on credentials passed in request body 
     var authToken = {result of your auth method} 

     response.Write(authToken); 

    } 
} 

Sus otros controladores heredan de un controlador de base de

public class BaseController : Controller 
{ 
    protected override void Execute(RequestContext requestContext) 
    { 
     var request = requestContext.HttpContext.Request; 
     var response = requestContext.HttpContext.Response; 

     var authToken = Request.Headers["Authorization"]; 

     // use token to authorize in your own method 
     var authorized = AmIAuthorized(); 

     if(authorized = false) { 
      response.StatusCode = 401; 
      response.Write("Invalid token"); 
      return;    
     } 

     response.StatusCode = 200; // OK 

     base.Execute(requestContext); // allow inheriting controller to continue 

    } 
} 

Muestra código para llamar a la API

public static void ExecutePostRequest(string contentType) 
     { 
      request = (HttpWebRequest)WebRequest.Create(Uri + Querystring); 
      request.Method = "POST"; 
      request.ContentType = contentType; // application/json usually 
      request.Headers["Authorization"] = token; 

      using (StreamWriter writer = new StreamWriter(request.GetRequestStream())) 
       writer.Write(postRequestData); 

      // GetResponse reaises an exception on http status code 400 
      // We can pull response out of the exception and continue on our way    
      try 
      { 
       response = (HttpWebResponse)request.GetResponse(); 
      } 
      catch (WebException ex) 
      { 
       response = (HttpWebResponse)ex.Response; 
      } 
      finally 
      { 
       using (StreamReader reader = 
        new StreamReader(response.GetResponseStream())) 
        responseText = reader.ReadToEnd(); 
       httpcontext = HttpContext.Current; 
      } 
     } 
+0

Jason, me gustaría más detalles. Soy muy nuevo en MVC (fondo RoR), así que no sé cómo funcionan los atributos de autorización. ¡Gracias! –

+0

Gracias Jason! ¿Cómo se "pasa la clave secreta de la API junto con el nombre de usuario y la contraseña en el cuerpo de la solicitud"? ¿Simplemente agrega encabezados HTTP personalizados a la solicitud o usa Http-Authorize? –

+0

Excelente, gracias por la respuesta detallada! –

Cuestiones relacionadas