# Authorize 屬性和 Ajax Request 的愛恨情仇 .... ###### tags: `被玩壞了` ## 發生情境 ### 使用 Ajax Call 後端時,遇到 Auth Cookie 已過期 按下搜尋後,發送 Ajax Request 請求,得到的 StatusCode 為302 (因為被重新導向至 Login 畫面) 也因為不是錯誤,Ajax 理所當然的將 Login 畫面丟至原本預留的位置...(如圖)  --- ## 測試過程 ### 首先,前端 Ajax 必須配置 error function,準備接收 401 狀態碼,重新導向 ``` $.ajax({ url: '@Url.Action("PagedPartial", "Member")', data: filter, type: 'Post', success: function (resultHtml) { // 取得資料後將目前 hash 重設 window.location.hash = page; // 將 PartialView 資料寫入 div id="MemberDatas" 的區塊 $('#MemberDatas').html(resultHtml); // 重設頁碼按鈕 $('#MemberDatas .pagination li a').each(function (i, item) { // 若是有超連結的頁碼 var hyperLinkUrl = $(item).attr('href'); if (typeof hyperLinkUrl !== 'undefined' && hyperLinkUrl !== false) { // 取得當前按鈕(<a>)的對應頁碼 var pageNumber = $(item).attr('href').replace('/Member?page=', ''); // 將頁碼按鈕的 href 去除 $(item).attr('href', '#'); // 設定按下頁碼事件 $(item).click(function (event) { event.preventDefault(); fetchPage(pageNumber); }); } }); }, error: function (xhr) { switch (xhr.status) { case 401: // 導至 Login 頁面,而且帶有 ReturnUrl window.location.href = "@Url.Action("Login","AdminUser", new { ReturnUrl = this.Context.Request.Path })" default: break; } } }); ``` ### 在 BaseController 中擷取 OnAuthorization 事件重新配置,遇到了什麼問題? * 如果配置 filterContext.Result,**一樣會變成 302**,有導向到 Login 畫面 * 設定 response.StatusCode = 401; response.End(); **會正常拋回 401,但會拋 Exception => 傳送 HTTP 標頭後,伺服器無法設定狀態。** ``` protected override void OnAuthorization(AuthorizationContext filterContext) { // 檢查 Action 有無 Auth 屬性 var isNeedAuth_Action = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), true).Any(); // 檢查 Controller 有無 Auth 屬性 var isNeedAuth_Controller = filterContext.Controller.GetType().GetCustomAttributes(typeof(AuthorizeAttribute), true).Any(); // 是否需要驗證的網頁 var isNeedAuth = isNeedAuth_Action || isNeedAuth_Controller; var httpContext = filterContext.HttpContext; var request = httpContext.Request; var response = httpContext.Response; if (isNeedAuth && !filterContext.HttpContext.User.Identity.IsAuthenticated && request.IsAjaxRequest()) { // 如果配置 filterContext.Result,一樣會變成 302,有導向到 Login 畫面 //filterContext.Result = new RedirectResult($"/AdminUser/Login"); //filterContext.Result = new HttpUnauthorizedResult(); //filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Unauthorized, "未登入或登入逾時"); //↓測試之後沒用,一樣會出現 => 傳送 HTTP 標頭後,伺服器無法設定狀態。 //response.BufferOutput = true; //response.Clear(); // 可以正常回傳 401 沒錯,但會出現 => 傳送 HTTP 標頭後,伺服器無法設定狀態。 response.StatusCode = 401; response.End(); } else { base.OnAuthorization(filterContext); } } ``` #### 結論 不適用,也就不分析優缺點 ### 自定義 ActionFilter => AjaxAuthorizeAttribute,擷取 HandleUnauthorizedRequest ( 處理授權失敗的HTTP請求 ) ``` public class AjaxAuthorizeAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { var httpContext = filterContext.HttpContext; var request = httpContext.Request; var response = httpContext.Response; if (request.IsAjaxRequest()) { response.StatusCode = (int)HttpStatusCode.Unauthorized; response.End(); } else { base.HandleUnauthorizedRequest(filterContext); } } } ``` #### 結論 適用。 * 優點:搭配 Ajax 可以正常運作了 * 缺點:無法在 Controller 上加入 [Authorize] 屬性,需要個別加入在 Action 上 ( 若為 Ajax 呼叫的 Action 是使用 [AjaxAuthorize] ) ## 參考 https://dotblogs.com.tw/shadow/2014/05/04/144960
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.