2012-04-26 11 views
6

Ho un applicazione Web Forms, che sto cercando di utilizzare la nuova versione beta Web API con. Gli endpoint che sto esponendo dovrebbero essere disponibili solo per un utente autenticato del sito poiché sono per uso AJAX. Nel mio web.config ho impostato per rifiutare tutti gli utenti a meno che non siano autenticati. Funziona come dovrebbe con Web Form ma non funziona come previsto con MVC o l'API Web.Utilizzo di autenticazione basata su form con il Web API

Ho creato un controller MVC e un controller API Web per testare con. Quello che sto vedendo è che non posso accedere al MVC o endpoint API Web affittassimo ho autenticare ma poi posso continuare a colpire quei punti finali, anche dopo la chiusura mio browser e recyling la piscina app. Ma se ho colpito una delle mie pagine aspx, che mi rimanda alla mia pagina di accesso, allora non posso colpire gli endpoint MVC o Web API fino a quando non riesco ad autenticare di nuovo.

C'è una ragione per cui MVC e Web API non funzionano come le mie pagine ASPX sono una volta che la mia sessione è invalidata? A giudicare da ciò, solo la richiesta ASPX sta cancellando il mio cookie di Autenticazione moduli, che presumo sia il problema qui.

+2

condividere alcune config, code ... – Aliostad

risposta

-1

Se si utilizza l'attributo MVC Autorizza, dovrebbe funzionare allo stesso modo per WebAPI come per i normali controller MVC.

+3

No, l'autorizzazione MVC attributi e quelli WebAPI sono totalmente differenti. – Jez

3

Se il Web API è solo usato all'interno di un'applicazione MVC esistente, il mio consiglio è quello di creare un filtro personalizzato AuthorizeAttribute per entrambi i controller MVC e WebAPI; Creo quello che io chiamo un filtro "AuthorizeSafe", che blacklist tutto di default in modo che se si dimentica di applicare un attributo autorizzazione al controller o metodo di accesso, si è negato (penso che l'approccio di default whitelist è insicuro).

Sono disponibili due classi di attributi da estendere; System.Web.Mvc.AuthorizeAttribute e System.Web.Http.AuthorizeAttribute; il primo è utilizzato con l'autenticazione forme MVC e quest'ultimo aggancia anche in forme di autenticazione (questo è molto bello perché significa che non c'è bisogno di andare costruire una intera architettura di autenticazione separato per l'autenticazione e l'autorizzazione API). Ecco cosa mi è venuto in mente: nega l'accesso a tutti i controller/azioni MVC e ai controller/azioni WebApi per impostazione predefinita a meno che non venga applicato un attributo o AuthorizeSafe. In primo luogo, un metodo di estensione per aiutare con attributi personalizzati:

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 classe di autorizzazione di supporto che entrambe le AuthorizeAttribute estensioni usano:

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, 
} 

Le due classi di estensione stessi:

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; 
     } 
    } 
} 

E infine, l'attributo che può essere applicato a metodi/controller per consentire agli utenti in determinati ruoli di accedervi:

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

Poi registriamo i nostri filtri "AuthorizeSafe" a livello globale da 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()); 
    } 

Poi ad aprire un azione per es.l'accesso anonimo o accesso solo Admin:

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

Sta usando webforms non mvc ... funziona così? – bbqchickenrobot

+0

@bbqchickenrobot, ho applicato questa soluzione a un progetto API Web puro. Devi ignorare parte del codice specifico MVC. – zacharydl