WebApi-Identity 自訂Authorize可以分辨何時回傳401何時回傳403
前言
之前有紀錄了一些identity的文章,因為我有修改了一些程式碼,所以之後會再編修過,但當我們在使用[Author]或[Author(Roles = "Admin")的時候,其實只要驗證沒通過,都一律只會回傳401,這讓我在前端要分辨是否未登入或只是權限不足的時候,沒辦法分辨這個狀態要做何處理,因為已找到解決方案,所以紀錄一下,如果有人一樣遇到這種問題,希望會有幫助,當我實作了token驗證,還有角色認證之後,通常會在web api的部份,直接寫attribute,如下圖所示。
然後我有一個帳戶是只有user權限的,並沒有Admin的權限,當我判斷401的時候,會自動登出,但權限不足也會導到登入頁,就會造成很大的麻煩,有些時候為了避掉這種麻煩,可能就會在前端去鎖功能,如果我今天希望的是後端驗證就做完判斷的話,那勢必就得解決這種問題,下面是我的例子圖示。
我目前登入狀態是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,如下圖所示
最後的結果如下,我使用anson登入
當我想要查看所有使用者的資訊,因為權限不足就會順利回傳403
以上紀錄一下,如果有更好的做法,再請多多指教。
以下是原本參考的文章,但因為有些許錯誤,所以我自己有參考原本的原始碼稍稍修改過,附上連結供參考。