Asp.net

AJAX 和 FormsAuthentication,如何防止 FormsAuthentication 覆蓋 HTTP 401?

  • September 23, 2011

在一個配置了 FormsAuthentication 的應用程序中,當使用者在沒有 auth cookie 或使用過期 cookie 的情況下訪問受保護的頁面時,ASP.NET 會發出 HTTP 401 Unauthorized,然後 FormsAuthentication 模組會在請求結束之前攔截此響應,並將其更改為找到 HTTP 302,設置 HTTP 標頭“位置:/path/loginurl”以將使用者代理重定向到登錄頁面,然後瀏覽器轉到該頁面並檢索未受保護的登錄頁面,獲得 HTTP 200 好的。

當沒有考慮 AJAX 時,這確實是一個非常好的主意。

現在我的應用程序中有一個返回 JSON 數據的 url,它需要對使用者進行身份驗證。一切正常,問題是如果 auth cookie 過期,當我的客戶端程式碼呼叫伺服器時,它將通過登錄頁面的 html 獲得 HTTP 200 OK,而不是 HTTP 401 Unauthorized(因為前面解釋過)。然後我的客戶端試圖將登錄頁面 html 解析為 json,但失敗了。

那麼問題是:如何處理來自客戶端的過期身份驗證?應對這種情況的最優雅的解決方案是什麼?我需要知道呼叫何時成功,我想使用 HTTP 語義來完成。

是否可以以安全的跨瀏覽器方式從客戶端讀取自定義 HTTP 標頭?如果請求是 AJAX 請求,有沒有辦法告訴 FormsAuthenticationModule 不執行重定向?有沒有辦法像覆蓋 HTTP 請求方法一樣使用 HTTP 標頭覆蓋 HTTP 狀態?

我需要表單身份驗證,並且我想避免重寫該模組或編寫我自己的表單身份驗證模組。

問候。

我遇到了同樣的問題,不得不在 MVC 中使用自定義屬性。您可以輕鬆地將其調整為在 Web 表單中工作,如果您的所有頁面都從某個基本頁面繼承(MVC 中的全域屬性允許相同的事情 - 覆蓋所有控制器/操作的 OnAuthorization 方法),則可以覆蓋基本頁面中頁面的授權應用)

這是屬性的樣子:

public class AjaxAuthorizationAttribute : FilterAttribute, IAuthorizationFilter
   {
       public void OnAuthorization(AuthorizationContext filterContext)
       {
           if (filterContext.HttpContext.Request.IsAjaxRequest()
               && !filterContext.HttpContext.User.Identity.IsAuthenticated
               && (filterContext.ActionDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), true).Count() > 0
               || filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), true).Count() > 0))
           {
               filterContext.HttpContext.SkipAuthorization = true;
               filterContext.HttpContext.Response.Clear();
               filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
               filterContext.Result = new HttpUnauthorizedResult("Unauthorized");
               filterContext.Result.ExecuteResult(filterContext.Controller.ControllerContext);
               filterContext.HttpContext.Response.End();
           }
       }
   }

請注意,您需要呼叫 HttpContext.Response.End(); 或者您的請求將被重定向到登錄(因此我失去了一些頭髮)。

在客戶端,我使用了 jQuery ajaxError 方法:

var lastAjaxCall = { settings: null, jqXHR: null };
var loginUrl = "yourloginurl";

//...
//...

$(document).ready(function(){
   $(document).ajaxError(function (event, jqxhr, settings) {
           if (jqxhr.status == 401) {
               if (loginUrl) {
                   $("body").prepend("<div class='loginoverlay'><div class='full'></div><div class='iframe'><iframe id='login' src='" + loginUrl + "'></iframe></div></div>");
                   $("div.loginoverlay").show();
                   lastAjaxCall.jqXHR = jqxhr;
                   lastAjaxCall.settings = settings;
               }
           }
   }

}

這顯示了在目前頁面上的 iframe 中登錄(看起來使用者已被重定向,但您可以使其不同),當登錄成功時,此彈出視窗已關閉,並重新發送原始 ajax 請求:

if (lastAjaxCall.settings) {
       $.ajax(lastAjaxCall.settings);
       lastAjaxCall.settings = null;
   }

這允許您的使用者在會話到期時登錄,而不會失去他們在最後顯示的表單中輸入的任何工作或數據。

引用自:https://stackoverflow.com/questions/7532261