<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          在 .NET 5.0 中自定義授權(quán)響應(yīng)

          共 5704字,需瀏覽 12分鐘

           ·

          2021-08-11 12:18


          在 .NET 5.0 中自定義授權(quán)響應(yīng)

          ASP.NET Core 授權(quán)框架中經(jīng)常要求的[1]一項(xiàng)功能是能夠在授權(quán)失敗時(shí)自定義 HTTP 響應(yīng)。

          以前,唯一的方法是IAuthorizationService直接在您的控制器中(或通過(guò)過(guò)濾器)調(diào)用授權(quán)服務(wù) ,類似于基于資源的授權(quán)方法[2]實(shí)現(xiàn)您自己的授權(quán)過(guò)濾器[3]

          從 .NET 5.0 開(kāi)始,您現(xiàn)在可以通過(guò)實(shí)現(xiàn)IAuthorizationMiddlewareResultHandler接口來(lái)自定義 HTTP 響應(yīng);當(dāng)授權(quán)失敗時(shí),授權(quán)框架會(huì)自動(dòng)調(diào)用中間件。

          這是 記錄[4]在微軟文檔的網(wǎng)站,但根據(jù)我的具體使用情況我花了不少時(shí)間才找到。

          問(wèn)題

          我一直在采取措施將舊的 ASP.NET Web API 應(yīng)用程序移植到 .NET Core 5.0。此 API 具有分層 URI 結(jié)構(gòu),因此大多數(shù)端點(diǎn)將位于“站點(diǎn)”資源下,例如:

          ?/sites?/sites/{siteId}?/sites/{siteId}/blog

          為了驗(yàn)證用戶是否有權(quán)訪問(wèn)指定站點(diǎn),該應(yīng)用程序以前使用自定義操作過(guò)濾器來(lái)提取siteId路由參數(shù)并根據(jù)用戶的聲明對(duì)其進(jìn)行驗(yàn)證。遷移到 .NET 5.0 我想利用授權(quán)框架來(lái)實(shí)現(xiàn)這種基于資源的授權(quán),但同樣不想在每個(gè)控制器中復(fù)制這個(gè)邏輯。

          我的解決方案是實(shí)現(xiàn)一個(gè)執(zhí)行類似操作的授權(quán)處理程序,獲取siteId參數(shù)并驗(yàn)證用戶的訪問(wèn)權(quán)限:

          public class SiteAccessAuthorizationHandler : AuthorizationHandler<SiteAccessRequirement>{    private const string SiteIdRouteParameter = "siteId";    private readonly ILogger<SiteAccessAuthorizationHandler> _logger;
          public SiteAccessAuthorizationHandler(ILogger<SiteAccessAuthorizationHandler> logger) { _logger = logger.NotNull(nameof(logger)); }
          protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SiteAccessRequirement requirement) { context.NotNull(nameof(context)); requirement.NotNull(nameof(requirement));
          if (context.Resource is HttpContext httpContext && httpContext.GetRouteData().Values.TryGetValue(SiteIdRouteParameter, out object? routeValue) && routeValue is string siteId) { string qualifiedId = $"sites/{siteId}"; AccountPrincipal account = context.User.ToAccount();
          _logger.LogDebug("Validating access to Site {SiteId} from User {UserId}.", qualifiedId, account.GetAuthIdentifier());
          if (account.CanAccessSite(qualifiedId)) { context.Succeed(requirement); } else { _logger.LogWarning("Site validation failed. User {UserId} is not permitted to access {SiteId}.", account.GetAuthIdentifier(), qualifiedId); } }
          return Task.CompletedTask; }}

          然后將其注冊(cè)為授權(quán)策略的一部分:

          services.AddAuthorization(options =>{                    options.FallbackPolicy = Policies.FallbackPolicy;    options.AddPolicy("SiteAccess", Policies.SiteAccessPolicy);})
          public static AuthorizationPolicy SiteAccessPolicy => ConfigureDefaults(new AuthorizationPolicyBuilder()) .AddRequirements(new SiteAccessRequirement()) .Build();
          private static AuthorizationPolicyBuilder ConfigureDefaults(AuthorizationPolicyBuilder builder) => builder.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .RequireClaim(JwtClaimTypes.ClientId);

          并應(yīng)用于控制器和/或動(dòng)作:

          [Authorize(Policy = "SiteAccess")][HttpGet("{siteId}", Name = RouteNames.SiteRoute)]public async Task<IActionResult> GetSiteAsync(string siteId, CancellationToken cancellationToken){    var site = await _session.LoadAsync<CMS.Domain.Site>($"sites/{siteId}", cancellationToken);    return site is null ? NotFound() : Ok(Enrich(_mapper.Map<Site>(site), true));}

          當(dāng)我嘗試訪問(wèn)未映射到當(dāng)前用戶的站點(diǎn)時(shí),我會(huì)收到HTTP 403 - Forbidden響應(yīng)。

          這樣雖然達(dá)到了保護(hù)站點(diǎn)資源的目的,但也存在泄露用戶無(wú)權(quán)訪問(wèn)的站點(diǎn)信息的弊端。因此最好返回一個(gè)HTTP 404 - Not Found響應(yīng)。考慮到該站點(diǎn)不存在于用戶的站點(diǎn)資源集合中,這在語(yǔ)義上也是有意義的。

          如果您想知道為什么我不只是將用戶過(guò)濾器作為查詢的一部分,那是因?yàn)橛脩?帳戶與內(nèi)容域是分開(kāi)的,并且由于數(shù)據(jù)模型的設(shè)計(jì)以及我使用的事實(shí)鍵值存儲(chǔ),驗(yàn)證訪問(wèn)的責(zé)任轉(zhuǎn)移到應(yīng)用層。

          解決方案

          為了實(shí)現(xiàn)上述目標(biāo),我們可以使用 newIAuthorizationMiddlewareResultHandler并創(chuàng)建一個(gè)處理程序,當(dāng)由于我的站點(diǎn)訪問(wèn)要求未得到滿足而導(dǎo)致授權(quán)失敗時(shí),該處理程序會(huì)轉(zhuǎn)換 HTTP 響應(yīng):

          public class AuthorizationResultTransformer : IAuthorizationMiddlewareResultHandler{    private readonly IAuthorizationMiddlewareResultHandler _handler;
          public AuthorizationResultTransformer() { _handler = new AuthorizationMiddlewareResultHandler(); }
          public async Task HandleAsync( RequestDelegate requestDelegate, HttpContext httpContext, AuthorizationPolicy authorizationPolicy, PolicyAuthorizationResult policyAuthorizationResult) { if (policyAuthorizationResult.Forbidden && policyAuthorizationResult.AuthorizationFailure != null) { if (policyAuthorizationResult.AuthorizationFailure.FailedRequirements.Any(requirement => requirement is SiteAccessRequirement)) { httpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; return; }
          // Other transformations here }
          await _handler.HandleAsync(requestDelegate, httpContext, authorizationPolicy, policyAuthorizationResult); }}

          在上面的代碼中,我檢查授權(quán)失敗(結(jié)果是禁止)和失敗的要求,相應(yīng)地更改HTTP狀態(tài)代碼;否則我們通過(guò)調(diào)用內(nèi)置的AuthorizationMiddlewareResultHandler.

          為了連接自定義處理程序,它在啟動(dòng)時(shí)注冊(cè):

          services.AddAuthorization(options =>{                    options.FallbackPolicy = Policies.FallbackPolicy;    options.AddPolicy("SiteAccess", Policies.SiteAccessPolicy);}).AddSingleton<IAuthorizationMiddlewareResultHandler, AuthorizationResultTransformer>();

          References

          [1] 經(jīng)常要求的: https://github.com/dotnet/aspnetcore/issues/4670
          [2] 基于資源的授權(quán)方法: https://docs.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-5.0
          [3] 實(shí)現(xiàn)您自己的授權(quán)過(guò)濾器: https://ignas.me/tech/custom-unauthorized-response-body/
          [4] 記錄: https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/customizingauthorizationmiddlewareresponse?view=aspnetcore-5.0


          往期精彩回顧




          【推薦】.NET Core開(kāi)發(fā)實(shí)戰(zhàn)視頻課程 ★★★

          .NET Core實(shí)戰(zhàn)項(xiàng)目之CMS 第一章 入門(mén)篇-開(kāi)篇及總體規(guī)劃

          【.NET Core微服務(wù)實(shí)戰(zhàn)-統(tǒng)一身份認(rèn)證】開(kāi)篇及目錄索引

          Redis基本使用及百億數(shù)據(jù)量中的使用技巧分享(附視頻地址及觀看指南)

          .NET Core中的一個(gè)接口多種實(shí)現(xiàn)的依賴注入與動(dòng)態(tài)選擇看這篇就夠了

          10個(gè)小技巧助您寫(xiě)出高性能的ASP.NET Core代碼

          用abp vNext快速開(kāi)發(fā)Quartz.NET定時(shí)任務(wù)管理界面

          在ASP.NET Core中創(chuàng)建基于Quartz.NET托管服務(wù)輕松實(shí)現(xiàn)作業(yè)調(diào)度

          現(xiàn)身說(shuō)法:實(shí)際業(yè)務(wù)出發(fā)分析百億數(shù)據(jù)量下的多表查詢優(yōu)化

          關(guān)于C#異步編程你應(yīng)該了解的幾點(diǎn)建議

          C#異步編程看這篇就夠了

          瀏覽 46
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲日本香蕉 | 三级理论网站 | 男女尻屄| 色婷婷香蕉 | 制服.丝袜.亚洲.中文 |