2012-04-26 12 views
6

Mam aplikację Web Forms, z którą próbuję korzystać z nowego interfejsu API WWW. Punkty końcowe, które ujawniam, powinny być dostępne tylko dla uwierzytelnionego użytkownika witryny, ponieważ są one przeznaczone do użycia w AJAX. W moim pliku konfiguracyjnym web.config ustawiono opcję blokowania wszystkich użytkowników, chyba że są oni uwierzytelnieni. Działa to tak, jak powinno z Web Forms, ale nie działa zgodnie z oczekiwaniami w MVC lub Web API.Korzystanie z uwierzytelniania za pomocą formularzy z interfejsem WWW

Stworzyłem zarówno kontroler MVC, jak i kontroler Web API do przetestowania. Widzę, że nie mogę uzyskać dostępu do punktów końcowych MVC lub Web API, dopóki nie będę uwierzytelniać, ale mogę kontynuować wybieranie tych punktów końcowych, nawet po zamknięciu przeglądarki i ponownym przetworzeniu puli aplikacji. Ale jeśli trafię na jedną ze stron aspx, która odsyła mnie do mojej strony logowania, nie mogę trafić na punkty końcowe MVC lub Web API, dopóki nie będę ponownie uwierzytelniać.

Czy istnieje powód, dla którego MVC i Web API nie działają, ponieważ moje strony ASPX są po unieważnieniu mojej sesji? Wygląda na to, że tylko żądanie ASPX usuwa moje ciasteczko uwierzytelniające formularze, co zakładam tutaj.

+2

Udostępnij niektóre konfiguracje, kod ... – Aliostad

Odpowiedz

-1

Jeśli używasz atrybutu autoryzacji MVC, powinien działać tak samo dla interfejsu WebAPI jak dla normalnych kontrolerów MVC.

+3

Nie, atrybuty autoryzacji MVC i WebApi są zupełnie inne. – Jez

3

Jeśli web API jest używany tylko w ramach istniejącej aplikacji MVC, moja rada jest, aby utworzyć niestandardowy AuthorizeAttribute filtr dla obu kontrolerów MVC i WebAPI; Tworzę coś, co nazywam filtrem "AuthorizeSafe", który domyślnie umieszcza wszystko na czarnej liście, więc jeśli zapomnisz zastosować atrybut autoryzacji do kontrolera lub metody, odmawiasz dostępu (chyba domyślne podejście do białej listy jest niepewne).

Dostępne są dwie klasy atrybutów do przedłużenia; System.Web.Mvc.AuthorizeAttribute i System.Web.Http.AuthorizeAttribute; pierwsza z nich jest używana z uwierzytelnianiem formularzy MVC, a druga z haczykiem do uwierzytelniania formularzy (jest to bardzo dobre, ponieważ oznacza to, że nie musisz budować oddzielnej architektury uwierzytelniania dla uwierzytelniania i autoryzacji API). Oto, co wymyśliłem - domyślnie odmawia dostępu do wszystkich kontrolerów/działań MVC i kontrolerów/akcji WebApi, chyba że zastosowano atrybut AllowAnonymous lub AuthorizeSafe. Po pierwsze, metoda rozszerzenie pomocy z niestandardowych atrybutów:

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

Klasa pomocnika autoryzacja że oba rozszerzenia AuthorizeAttribute użyć:

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

dwóch klas przedłużających się:

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

I wreszcie atrybut, który można zastosować do metod/kontrolerów, aby umożliwić użytkownikom w określonych rolach dostęp do nich:

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

Następnie rejestrujemy nasze filtry "AuthorizeSafe" globalnie od 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()); 
    } 

Następnie otworzyć skargę do np.anonimowy dostęp lub tylko dostęp administracyjny:

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

Używa on webformów, a nie mvc ... czy to działa tak samo? – bbqchickenrobot

+0

@ bbqchickenrobot, zastosowałem to rozwiązanie do czystego projektu Web API. Musisz zignorować część kodu specyficznego dla MVC. – zacharydl

Powiązane problemy