2012-04-26 29 views
6

Tengo una aplicación de formularios web con la que intento usar la nueva API Web API. Los puntos finales que expongo solo deben estar disponibles para un usuario autenticado del sitio, ya que son para uso de AJAX. En mi web.config lo tengo configurado para denegar a todos los usuarios a menos que estén autenticados. Esto funciona como debería con Web Forms pero no funciona como se esperaba con MVC o Web API.Uso de la autenticación de formularios con la API web

He creado tanto un controlador MVC como un controlador API web para probar. Lo que estoy viendo es que no puedo acceder a los puntos finales MVC o Web API hasta que me autentique, pero luego puedo seguir accediendo a esos puntos finales, incluso después de cerrar el navegador y reciclar el grupo de aplicaciones. Pero si toco una de mis páginas aspx, que me devuelve a mi página de inicio de sesión, entonces no puedo acceder a los puntos finales MVC o Web API hasta que me autentique de nuevo.

¿Hay alguna razón por la cual MVC y Web API no funcionen ya que mis páginas ASPX una vez que se invalida mi sesión? Por lo que parece, solo la solicitud ASPX está borrando mi cookie de autenticación de formularios, que asumo es el problema aquí.

+2

Compartir alguna configuración, código ... Sin – Aliostad

Respuesta

-1

Si está utilizando el atributo MVC Authorize debería funcionar de la misma manera para la WebAPI que para los controladores MVC normales.

+3

, atributos de autorización MVC y los WebAPI son totalmente diferentes. – Jez

3

Si su web API sólo se utiliza dentro de una aplicación MVC existente, mi consejo es crear un filtro personalizado AuthorizeAttribute tanto para sus controladores MVC y WebAPI; Creo lo que llamo un filtro "AuthorizeSafe", que pone en la lista negra todo de manera predeterminada, de modo que si olvida aplicar un atributo de autorización al controlador o método, se le niega el acceso (creo que el enfoque de la lista blanca predeterminada es inseguro).

Se proporcionan dos clases de atributos para extender; System.Web.Mvc.AuthorizeAttribute y System.Web.Http.AuthorizeAttribute; el primero se usa con la autenticación de formularios MVC y el último también se conecta a la autenticación de formularios (esto es muy bueno porque significa que no tiene que construir una arquitectura de autenticación separada para la autenticación y autorización de su API). Esto es lo que se me ocurrió: niega el acceso a todos los controladores/acciones MVC y a los controladores/acciones de WebApi de forma predeterminada a menos que se aplique un atributo AllowAnonymous o AuthorizeSafe. En primer lugar, un método de extensión para ayudar con atributos personalizados:

public static class CustomAttributeProviderExtensions { 
    public static List<T> GetCustomAttributes<T>(this ICustomAttributeProvider provider, bool inherit) where T : Attribute { 
     List<T> attrs = new List<T>(); 

     foreach (object attr in provider.GetCustomAttributes(typeof(T), false)) { 
      if (attr is T) { 
       attrs.Add(attr as T); 
      } 
     } 

     return attrs; 
    } 
} 

La clase de autorización de ayuda que tanto los AuthorizeAttribute extensiones utilizan:

public static class AuthorizeSafeHelper { 
    public static AuthActionToTake DoSafeAuthorization(bool anyAllowAnonymousOnAction, bool anyAllowAnonymousOnController, List<AuthorizeSafeAttribute> authorizeSafeOnAction, List<AuthorizeSafeAttribute> authorizeSafeOnController, out string rolesString) { 
     rolesString = null; 

     // If AllowAnonymousAttribute applied to action or controller, skip authorization 
     if (anyAllowAnonymousOnAction || anyAllowAnonymousOnController) { 
      return AuthActionToTake.SkipAuthorization; 
     } 

     bool foundRoles = false; 
     if (authorizeSafeOnAction.Count > 0) { 
      AuthorizeSafeAttribute foundAttr = (AuthorizeSafeAttribute)(authorizeSafeOnAction.First()); 
      foundRoles = true; 
      rolesString = foundAttr.Roles; 
     } 
     else if (authorizeSafeOnController.Count > 0) { 
      AuthorizeSafeAttribute foundAttr = (AuthorizeSafeAttribute)(authorizeSafeOnController.First()); 
      foundRoles = true; 
      rolesString = foundAttr.Roles; 
     } 

     if (foundRoles && !string.IsNullOrWhiteSpace(rolesString)) { 
      // Found valid roles string; use it as our own Roles property and auth normally 
      return AuthActionToTake.NormalAuthorization; 
     } 
     else { 
      // Didn't find valid roles string; DENY all access by default 
      return AuthActionToTake.Unauthorized; 
     } 
    } 
} 

public enum AuthActionToTake { 
    SkipAuthorization, 
    NormalAuthorization, 
    Unauthorized, 
} 

Las dos clases de extensiones de sí mismos:

public sealed class AuthorizeSafeFilter : System.Web.Mvc.AuthorizeAttribute { 
    public override void OnAuthorization(AuthorizationContext filterContext) { 
     if (!string.IsNullOrEmpty(this.Roles) || !string.IsNullOrEmpty(this.Users)) { 
      throw new Exception("This class is intended to be applied to an MVC web API application as a global filter in RegisterWebApiFilters, not applied to individual actions/controllers. Use the AuthorizeSafeAttribute with individual actions/controllers."); 
     } 

     string rolesString; 
     AuthActionToTake action = AuthorizeSafeHelper.DoSafeAuthorization(
      filterContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(false).Count() > 0, 
      filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(false).Count() > 0, 
      filterContext.ActionDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>(false), 
      filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>(false), 
      out rolesString 
     ); 

     string rolesBackup = this.Roles; 
     try { 
      switch (action) { 
       case AuthActionToTake.SkipAuthorization: 
        return; 

       case AuthActionToTake.NormalAuthorization: 
        this.Roles = rolesString; 
        base.OnAuthorization(filterContext); 
        return; 

       case AuthActionToTake.Unauthorized: 
        filterContext.Result = new HttpUnauthorizedResult(); 
        return; 
      } 
     } 
     finally { 
      this.Roles = rolesBackup; 
     } 
    } 
} 

public sealed class AuthorizeSafeApiFilter : System.Web.Http.AuthorizeAttribute { 
    public override void OnAuthorization(HttpActionContext actionContext) { 
     if (!string.IsNullOrEmpty(this.Roles) || !string.IsNullOrEmpty(this.Users)) { 
      throw new Exception("This class is intended to be applied to an MVC web API application as a global filter in RegisterWebApiFilters, not applied to individual actions/controllers. Use the AuthorizeSafeAttribute with individual actions/controllers."); 
     } 

     string rolesString; 
     AuthActionToTake action = AuthorizeSafeHelper.DoSafeAuthorization(
      actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0, 
      actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0, 
      actionContext.ActionDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>().ToList(), 
      actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>().ToList(), 
      out rolesString 
     ); 

     string rolesBackup = this.Roles; 
     try { 
      switch (action) { 
       case AuthActionToTake.SkipAuthorization: 
        return; 

       case AuthActionToTake.NormalAuthorization: 
        this.Roles = rolesString; 
        base.OnAuthorization(actionContext); 
        return; 

       case AuthActionToTake.Unauthorized: 
        HttpRequestMessage request = actionContext.Request; 
        actionContext.Response = request.CreateResponse(HttpStatusCode.Unauthorized); 
        return; 
      } 
     } 
     finally { 
      this.Roles = rolesBackup; 
     } 
    } 
} 

Y finalmente, el atributo que se puede aplicar a los métodos/controladores para permitir a los usuarios en ciertos roles acceder a ellos:

public class AuthorizeSafeAttribute : Attribute { 
    public string Roles { get; set; } 
} 

Entonces Registramos nuestros filtros "AuthorizeSafe" globalmente desde Global.asax:

public static void RegisterGlobalFilters(GlobalFilterCollection filters) { 
     // Make everything require authorization by default (whitelist approach) 
     filters.Add(new AuthorizeSafeFilter()); 
    } 

    public static void RegisterWebApiFilters(HttpFilterCollection filters) { 
     // Make everything require authorization by default (whitelist approach) 
     filters.Add(new AuthorizeSafeApiFilter()); 
    } 

Luego de abrir una acción a, por ejemplo.el acceso anónimo o sólo el acceso del administrador:

public class AccountController : System.Web.Mvc.Controller { 
    // GET: /Account/Login 
    [AllowAnonymous] 
    public ActionResult Login(string returnUrl) { 
     // ... 
    } 
} 

public class TestApiController : System.Web.Http.ApiController { 
    // GET API/TestApi 
    [AuthorizeSafe(Roles="Admin")] 
    public IEnumerable<TestModel> Get() { 
     return new TestModel[] { 
      new TestModel { TestId = 123, TestValue = "Model for ID 123" }, 
      new TestModel { TestId = 234, TestValue = "Model for ID 234" }, 
      new TestModel { TestId = 345, TestValue = "Model for ID 345" } 
     }; 
    } 
} 
+0

Está usando formularios web, no mvc ... ¿funciona de la misma manera? – bbqchickenrobot

+0

@bbqchickenrobot, apliqué esta solución a un proyecto de Web API puro. Tienes que ignorar algunos de los códigos específicos de MVC. – zacharydl

Cuestiones relacionadas