---
# System prepended metadata

title: Authorize 屬性和 Ajax Request 的愛恨情仇 ....
tags: [被玩壞了]

---

# Authorize 屬性和 Ajax Request 的愛恨情仇 ....
###### tags: `被玩壞了`

## 發生情境
### 使用 Ajax Call 後端時，遇到 Auth Cookie 已過期

按下搜尋後，發送 Ajax Request 請求，得到的 StatusCode 為302 (因為被重新導向至 Login 畫面)
也因為不是錯誤，Ajax 理所當然的將 Login 畫面丟至原本預留的位置...(如圖)

![](https://i.imgur.com/osDhndH.png)

---

## 測試過程
### 首先，前端 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