防止 FormsAuthenticationModule 攔截 ASP.NET Web API 響應
在 ASP.NET 中,FormsAuthenticationModule 攔截任何 HTTP 401,並將 HTTP 302 重定向返回到登錄頁面。這對 AJAX 來說很痛苦,因為您要求 json 並以 html 格式獲取登錄頁面,但狀態程式碼是 HTTP 200。
在 ASP.NET Web API 中避免這種攔截的方法是什麼?
在 ASP.NET MVC4 中,很容易通過顯式結束連接來防止這種攔截:
public class MyMvcAuthFilter:AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (filterContext.HttpContext.Request.IsAjaxRequest() && !filterContext.IsChildAction) { filterContext.Result = new HttpStatusCodeResult(401); filterContext.HttpContext.Response.StatusCode = 401; filterContext.HttpContext.Response.SuppressContent = true; filterContext.HttpContext.Response.End(); } else base.HandleUnauthorizedRequest(filterContext); } }但在 ASP.NET Web API 中,我無法顯式結束連接,因此即使我使用此程式碼,FormsAuthenticationModule 也會攔截響應並將重定向發送到登錄頁面:
public class MyWebApiAuth: AuthorizeAttribute { protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext) { if(actionContext.Request.Headers.Any(h=>h.Key.Equals("X-Requested-With",StringComparison.OrdinalIgnoreCase))) { var xhr = actionContext.Request.Headers.Single(h => h.Key.Equals("X-Requested-With", StringComparison.OrdinalIgnoreCase)).Value.First(); if (xhr.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase)) { // this does not work either //throw new HttpResponseException(HttpStatusCode.Unauthorized); actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized); return; } } base.HandleUnauthorizedRequest(actionContext); } }在 ASP.NET Web API 中避免這種行為的方法是什麼?我一直在看,我找不到這樣做的方法。
問候。
PS:我不敢相信這是 2012 年,這個問題仍然存在。
MVC 4 RC 的發行說明暗示自 Beta 版以來此問題已得到解決——您使用的是哪個?
http://www.asp.net/whitepapers/mvc4-release-notes 由 ASP.NET Web API 處理的未經授權的請求返回 401 Unauthroized:由 ASP.NET Web API 處理的未經授權的請求現在返回一個標準的 401 Unauthorized 響應,而不是重定向使用者代理到登錄表單,以便 Ajax 客戶端可以處理響應。
查看 MVC 的原始碼,似乎有一個通過 SuppressFormsAuthRedirectModule.cs 添加的功能
internal static bool GetEnabled(NameValueCollection appSettings) { // anything but "false" will return true, which is the default behavior所以看起來這是預設啟用的,RC應該在沒有任何英雄主義的情況下解決你的問題……作為一個側面,看起來你可以使用AppSettings http://d.hatena.ne.jp/shiba-禁用這個新模組嚴/20120430/1335787815:
<appSettings> <Add Key = "webapi:EnableSuppressRedirect" value = "false" /> </appSettings>編輯(範例和說明)
我現在在GitHub 上為這種方法創建了一個範例。新的重定向抑制要求您使用兩個正確的“授權”屬性;MVC 網路
$$ System.Web.Mvc.Authorize $$和網路 API$$ System.Web.Http.Authorize $$在控制器中 AND/OR 在全域過濾器Link中。 然而,這個例子確實引出了該方法的局限性。似乎 web.config 中的“授權”節點將始終優先於 MVC 路由,例如這樣的配置將覆蓋您的規則並仍然重定向到登錄:
<system.web> <authentication mode="Forms"> </authentication> <authorization> <deny users="?"/> //will deny anonymous users to all routes including WebApi </authorization> </system.web>遺憾的是,使用 Location 元素為某些 url 路由打開它似乎不起作用,WebApi 呼叫將繼續被攔截並重定向到登錄。
解決方案
對於 MVC 應用程序,我只是建議從 Web.Config 中刪除配置並在程式碼中堅持使用全域過濾器和屬性。
如果您必須在 Web.Config 中為 MVC 使用授權節點或具有混合 ASP.NET 和 WebApi 應用程序,那麼@PilotBob - 在下面的評論中 - 發現子文件夾和多個 Web.Config 可以用來做你的蛋糕和吃了它。
如果有人有興趣使用 Authorize 屬性處理 ASP.NET MVC 應用程序中的相同問題:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class Authorize2Attribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (filterContext.HttpContext.Request.IsAuthenticated) { filterContext.Result = new HttpStatusCodeResult((int) HttpStatusCode.Forbidden); } else { if (filterContext.HttpContext.Request.IsAjaxRequest()) { filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true; } base.HandleUnauthorizedRequest(filterContext); } } }這樣瀏覽器可以正確區分禁止和未經授權的請求。