前言
在.NET 5之前,當授權失敗即403時無法很友好的自定義錯誤資訊,以緻于比如利用Vue擷取到的是空響應,不能很好的處理實際業務,同時涉及到權限粒度控制到控制器、Action,也不能很好的擷取對應路由資訊。本文我們來看看在.NET 5中為何要出現針對授權失敗的中間件接口?它是如何一步步衍生出來的呢?以及對于授權失敗根據實際需要如何自定義響應錯誤,以及如何擷取對應路由資訊等等
授權失敗自定義響應資訊
如下是在.NET 5之前,對于授權處理,我們大多實作自定義的AuthorizationHandler
public class CustomAuthorizeHandler : AuthorizationHandler<CustomAuthorizationRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomAuthorizationRequirement requirement)
{
throw new NotImplementedException();
}
}
public class CustomAuthorizationRequirement : IAuthorizationRequirement
{
public CustomAuthorizationRequirement()
{
}
}
但此時參數給予的是授權上下文,我們并不能拿到目前請求上下文中的相關資訊,如果是在mvc中,想必大多是如下這般擷取的
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomAuthorizationRequirement requirement)
{
var context = context.Resource as HttpContext;
}
但對于前後分離的web api中,若我沒記錯的話,這樣是擷取到的是空,于是乎我們借助于注入上下文接口實作,演變成如下這樣
public class CustomAuthorizeHandler : AuthorizationHandler<CustomAuthorizationRequirement>
{
private readonly IHttpContextAccessor _accessor;
public CustomAuthorizeHandler(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
protected async override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomAuthorizationRequirement requirement)
{
var httpContext = _accessor.HttpContext;
// 授權失敗響應資訊
await httpContext.Response.WriteAsync("授權失敗");
//響應失敗調用
context.Fail();
}
}
通過上下文可以拿到比如使用者聲明資訊等等,貌似已經基本滿足我們實際業務需求,那要是我想擷取路由資訊又該如何呢?在3.0以下貌似隻能通過Path自己解析(個人猜測),從.NET Core 3.0+上,官方開放針對上下文的擴充方法,提供給我們擷取路由節點中繼資料詳細資訊

在該終結點類存在一個中繼資料屬性,該屬性為集合,該中繼資料包含任何你想要的東東
這裡必須強調一下,我最喜愛.NET Core的一點是,很多時候我們會封裝類庫,并在類庫中使用到Web APi中相關的上下文一切資訊等等,如果是以前.NET Framework怕是有點麻煩
比如如上在類庫中擷取上下文接口,如果你還是延續舊思想,檢視vs智能提示你是否需要安裝包,你會發現在Web APi中版本和你安裝的版本是對應不上的,這可能是有問題的哈(具體細節我并未深入探究),但實際上我想安裝的是.NET 5
在.NET Core類庫中要實作.NET Core相關基礎架構資訊,隻需要在類庫項目檔案中引入支援.NET Core應用程式包包即可,如此才和目前應用程式版本完全一緻
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
面向不同群體讀者,這裡還是重點強調下,以免有些學習.NET Core的童鞋走偏了!話題扯遠了,比如如上述我們想要擷取到中繼資料中的控制器和action名稱,該中繼資料集合參數都是object,是以我們想要對應的資訊,需要稍微清楚一點.NET Core基本流程處理所提供的各個對象
public class CustomAuthorizeHandler : AuthorizationHandler<CustomAuthorizationRequirement>
{
private readonly IHttpContextAccessor _accessor;
public CustomAuthorizeHandler(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
protected async override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomAuthorizationRequirement requirement)
{
var httpContext = _accessor.HttpContext;
var endPoint = httpContext.GetEndpoint();
var controllerActionDescriptor = (ControllerActionDescriptor)endPoint.Metadata
.ToList().FirstOrDefault(d => d is ControllerActionDescriptor);
var controllerName = controllerActionDescriptor.ControllerName;
var actionName = controllerActionDescriptor.ActionName;
}
}
講到這裡,實作對應抽象授權處理對象,基本上可滿足我們的需求,即使上述拿到上下文并響應,但是在接口響應上我們是擷取不到的,因為授權上下文,隻提供Fail和Succeed方法,要是我們想根據業務失敗後直接響應呢?是以最大的問題出在:我們無法完全控制響應,以及自定義響應。這個時候,經過開發者在github上激烈的回報,官方在.NET 5給出了,針對授權處理的中間件接口,上下文也已直接對外暴露
public class CustomAuthorizationMiddlewareResultHandler
: IAuthorizationMiddlewareResultHandler
{
public async Task HandleAsync(RequestDelegate next,
HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
{
var endPoint = context.GetEndpoint();
var controllerActionDescriptor = (ControllerActionDescriptor)endPoint.Metadata
.ToList().FirstOrDefault(d => d is ControllerActionDescriptor);
var controllerName = controllerActionDescriptor.ControllerName;
var actionName = controllerActionDescriptor.ActionName;
if (!context.User.Identity.IsAuthenticated)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsync("{\"data\":{\"succeeded\":false,\"code\":401,\"message\":\"登入已過期,請重新登入\"}}");
return;
}
else if (!await HandleRequirementEvaluateAsync(context.User, controllerName, actionName))
{
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
await context.Response.WriteAsync("{\"data\":{\"succeeded\":false,\"code\":403,\"message\":\"您暫無足夠的權限執行該操作\"}}");
return;
}
await next(context);
}
}
總結
自從.NET 5有了IAuthorizationMiddlewareResultHandler授權中間件接口,一切又是那麼得心應手!
你所看到的并非事物本身,而是經過诠釋後所賦予的意義