[Web API] Identity 自訂Authorize可以分辨何時回傳401何時回傳403

WebApi-Identity 自訂Authorize可以分辨何時回傳401何時回傳403

前言

 

之前有紀錄了一些identity的文章,因為我有修改了一些程式碼,所以之後會再編修過,但當我們在使用[Author]或[Author(Roles = "Admin")的時候,其實只要驗證沒通過,都一律只會回傳401,這讓我在前端要分辨是否未登入或只是權限不足的時候,沒辦法分辨這個狀態要做何處理,因為已找到解決方案,所以紀錄一下,如果有人一樣遇到這種問題,希望會有幫助,當我實作了token驗證,還有角色認證之後,通常會在web api的部份,直接寫attribute,如下圖所示。

 

image

 

然後我有一個帳戶是只有user權限的,並沒有Admin的權限,當我判斷401的時候,會自動登出,但權限不足也會導到登入頁,就會造成很大的麻煩,有些時候為了避掉這種麻煩,可能就會在前端去鎖功能,如果我今天希望的是後端驗證就做完判斷的話,那勢必就得解決這種問題,下面是我的例子圖示。

 

image

 

我目前登入狀態是kinanson所以有Admin的權限,但anson沒有此權限,所以當anson登入此頁面的時候,就會導到登入頁面,但我想要是未登入的回傳401,權限不足的只是回傳403,好讓js來判斷如何做處理。

 

 

自訂Author的filter

 

我google大神之後,終於找到解決方案,以下程式碼就可以讓有定義角色,權限不足是回傳403的狀態

 

using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Principal;
using System.Threading;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;


namespace Api.Filter
{
    public class NewAuthorize : AuthorizeAttribute
    {

        private static readonly string[] _emptyArray = new string[0];

        private readonly object _typeId = new object();

        private string _roles;
        private string[] _rolesSplit = _emptyArray;
        private string _users;
        private string[] _usersSplit = _emptyArray;

        new public string Roles
        {
            get { return _roles ?? String.Empty; }
            set
            {
                _roles = value;
                _rolesSplit = SplitString(value);
            }
        }

        new public string Users
        {
            get { return _users ?? String.Empty; }
            set
            {
                _users = value;
                _usersSplit = SplitString(value);
            }
        }

        protected override bool IsAuthorized(HttpActionContext actionContext)
        {
            if (actionContext == null)
            {
                throw new ArgumentNullException("actionContext");
            }

            IPrincipal user = HttpContext.Current.User;
            if (user == null || !user.Identity.IsAuthenticated)
            {
                return false;
            }

            return true;
        }

        protected bool IsAllowed(HttpActionContext actionContext)
        {
            if (actionContext == null)
            {
                throw new ArgumentNullException("actionContext");
            }

            IPrincipal user = Thread.CurrentPrincipal;
            if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
            {
                return false;
            }

            if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
            {
                return false;
            }

            return true;
        }

        public override void OnAuthorization(HttpActionContext actionContext)
        {
            if (actionContext == null)
            {
                throw new ArgumentNullException("actionContext");
            }

            if (SkipAuthorization(actionContext))
            {
                return;
            }

            if (!IsAuthorized(actionContext))
            {
                HandleUnauthorizedRequest(actionContext);
            }

            if (!IsAllowed(actionContext))
            {
                HandleForbiddenRequest(actionContext);
            }
        }
        protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
        {
            if (actionContext == null)
            {
                throw new ArgumentNullException("actionContext");
            }

            HttpResponseMessage result = new HttpResponseMessage()
            {
                StatusCode = HttpStatusCode.Unauthorized,
                RequestMessage = actionContext.Request
            };

            actionContext.Response = result;
        }

        protected void HandleForbiddenRequest(HttpActionContext actionContext)
        {
            if (actionContext == null)
            {
                throw new ArgumentNullException("actionContext");
            }

            HttpResponseMessage result = new HttpResponseMessage()
            {
                StatusCode = HttpStatusCode.Forbidden,
                RequestMessage = actionContext.Request
            };

            actionContext.Response = result;
        }

        private static bool SkipAuthorization(HttpActionContext actionContext)
        {
            if (actionContext == null)
            {
                throw new ArgumentNullException("actionContext");
            }

            return actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any()
                   || actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any();

        }

        internal static string[] SplitString(string original)
        {
            if (String.IsNullOrEmpty(original))
            {
                return _emptyArray;
            }

            var split = from piece in original.Split(',')
                        let trimmed = piece.Trim()
                        where !String.IsNullOrEmpty(trimmed)
                        select trimmed;
            return split.ToArray();
        }
    }

}

 

原始碼就不多說明,但其實這段原始碼可以讓我們了解不少有關author的細節,接著必須把原本的author改成自訂的attribute,如下圖所示

 

image

 

 

最後的結果如下,我使用anson登入

 

image

 

image

 

當我想要查看所有使用者的資訊,因為權限不足就會順利回傳403

 

image

 

 

以上紀錄一下,如果有更好的做法,再請多多指教。

 

以下是原本參考的文章,但因為有些許錯誤,所以我自己有參考原本的原始碼稍稍修改過,附上連結供參考。

 

https://gist.github.com/arthernan/5293014