2012-04-26 40 views
6

我有一個Web窗體應用程序,我試圖使用新的Web API測試版。我所公開的端點應該只對經過身份驗證的站點用戶開放,因爲他們用於AJAX。在我的web.config我已設置爲拒絕所有用戶,除非他們通過身份驗證。這與Web窗體應該一樣,但不能像MVC或Web API那樣正常工作。通過Web API使用窗體身份驗證

我創建了一個MVC控制器和Web API控制器來測試。我所看到的是我無法訪問MVC或Web API端點,直到我認證,但是我可以繼續點擊這些端點,即使在關閉瀏覽器並回收應用程序池後也是如此。但是,如果我點擊了一個aspx頁面,這會將我發回到我的登錄頁面,那麼我無法再次訪問MVC或Web API端點,直到我再次進行身份驗證。

是否有原因,爲什麼MVC和Web API不能正常工作,因爲我的ASPX頁面一旦我的會話失效?通過它的外觀,只有ASPX請求正在清除我的表單身份驗證cookie,我認爲這是這裏的問題。

+2

分享一些配置,代碼... – Aliostad 2012-04-26 09:01:47

回答

-1

如果您使用的是MVC Authorize屬性,它對於WebAPI應該與對於正常MVC控制器一樣。

+3

不,MVC授權屬性和WebApi的是完全不同的。 – Jez 2012-11-12 11:30:46

3

如果您的Web API只是現有的MVC應用程序中使用,我的建議是創建爲您的MVC和的WebAPI控制器定製AuthorizeAttribute過濾器;我創建了我稱之爲「AuthorizeSafe」的過濾器,默認情況下會將所有內容列入黑名單,這樣如果您忘記將控制器或方法的授權屬性應用於您,您將被拒絕訪問(我認爲默認白名單方法不安全)。

提供兩個屬性類供您擴展; System.Web.Mvc.AuthorizeAttributeSystem.Web.Http.AuthorizeAttribute;前者與MVC表單身份驗證一起使用,後者也與表單身份驗證掛鉤(這非常好,因爲這意味着您不必爲API驗證和授權構建完整的單獨身份驗證體系結構)。這是我想出的 - 它默認拒絕所有MVC控制器/操作和WebApi控制器/操作的訪問,除非應用了AllowAnonymousAuthorizeSafe屬性。首先,擴展方法來幫助自定義屬性:

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

AuthorizeAttribute擴展都使用授權輔助類:

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

兩個擴展類本身:

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

而且最後,可應用於方法/控制器以允許特定角色的用戶訪問它們的屬性:

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

然後我們在全球註冊了 「AuthorizeSafe」 過濾器從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()); 
    } 

然後打開到如一個動作。匿名訪問或只有管理員訪問:

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

他使用webforms不是MVC ...這是否工作是一樣的? – bbqchickenrobot 2013-04-26 21:00:54

+0

@bbqchickenrobot,我將此解決方案應用於純Web API項目。你必須忽略一些特定於MVC的代碼。 – zacharydl 2013-05-16 23:59:10