2012-04-26 4 views
6

Ich habe eine Web Forms-Anwendung, mit der ich versuche, die neue Web-API Beta zu verwenden. Die Endpunkte, die ich offenlege, sollten nur für einen authentifizierten Benutzer der Website verfügbar sein, da sie für AJAX-Zwecke verwendet werden. In meiner web.config habe ich eingestellt, alle Benutzer zu verweigern, wenn sie nicht authentifiziert sind. Dies funktioniert wie bei Web Forms, funktioniert aber nicht wie erwartet mit MVC oder der Web API.Verwenden der Formularauthentifizierung mit der Web-API

Ich habe sowohl einen MVC-Controller und Web-API-Controller zum Testen erstellt. Was ich sehe, ist, dass ich nicht auf die MVC- oder Web-API-Endpunkte zugreifen kann, bis ich mich authentifiziere, aber dann kann ich diese Endpunkte auch nach dem Schließen meines Browsers und der Wiederverwendung des App-Pools fortsetzen. Aber wenn ich auf eine meiner Aspx-Seiten klicke, die mich auf meine Anmeldeseite zurückschicken, kann ich die MVC- oder Web-API-Endpunkte nicht erreichen, bis ich mich erneut authentifiziere.

Gibt es einen Grund, warum MVC und Web-API nicht funktionieren, wie meine ASPX-Seiten sind, sobald meine Sitzung ungültig ist? Wie es aussieht, löscht nur die ASPX-Anfrage meinen Forms-Authentifizierungs-Cookie, von dem ich annehme, dass das Problem hier besteht.

+2

Teilen einige config Code ... – Aliostad

Antwort

-1

Wenn Sie das MVC Authorize-Attribut verwenden, sollte es für die WebAPI genauso funktionieren wie für normale MVC-Controller.

+3

Nein, MVC Autorisierungsattribute und WebAPI diejenigen sind völlig anders. – Jez

3

Wenn Ihr Web-API nur innerhalb einer bestehenden MVC-Anwendung verwendet wird, ist mein Rat einen benutzerdefinierten AuthorizeAttribute Filter für beide Ihre MVC und WebAPI-Controller zu erstellen; Ich erstelle einen so genannten "AuthorizeSafe" -Filter, der standardmäßig alle Blacklists auflistet. Wenn Sie also vergessen, ein Autorisierungsattribut auf den Controller oder die Methode anzuwenden, wird Ihnen der Zugriff verweigert (ich denke, der Standard-Whitelist-Ansatz ist unsicher).

Zwei Attributklassen stehen zur Erweiterung zur Verfügung; System.Web.Mvc.AuthorizeAttribute und System.Web.Http.AuthorizeAttribute; Ersteres wird bei der MVC-Formularauthentifizierung verwendet, letzteres auch bei der Formularauthentifizierung (das ist sehr schön, da Sie keine vollständige Authentifizierungsarchitektur für die API-Authentifizierung und -Autorisierung erstellen müssen). Hier ist, was ich gefunden habe - es verweigert den Zugriff auf alle MVC-Controller/Aktionen und WebApi-Controller/Aktionen standardmäßig, es sei denn, ein AllowAnonymous oder AuthorizeSafe Attribut wird angewendet. Zunächst wird eine Erweiterungsmethode mit benutzerdefinierten helfen Attribute:

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

Die Genehmigung Helfer-Klasse, die beide die AuthorizeAttribute Erweiterungen verwenden:

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

Die beiden Erweiterungsklassen selbst:

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

Und schließlich das Attribut, das auf Methoden/Controller angewendet werden kann, damit Benutzer in bestimmten Rollen auf sie zugreifen können:

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

Dann registrieren wir unsere "AuthorizeSafe" Filter global von 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()); 
    } 

Dann eine Aktion zB zu öffnen.anonymen Zugriff oder nur Admin-Zugang:

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

Er verwendet Webforms nicht mvc ... funktioniert das gleich? – bbqchickenrobot

+0

@bbqchickenrobot, ich habe diese Lösung auf ein reines Web-API-Projekt angewendet. Sie müssen einige der MVC-spezifischen Code ignorieren. – zacharydl