.net core 自定義授權策略提供程式進行許可權驗證

来源:https://www.cnblogs.com/lonely-wen/archive/2023/03/23/17247847.html
-Advertisement-
Play Games

.net core 自定義授權策略提供程式進行許可權驗證 在這之前先瞭解一下鑒權和授權的概念; 鑒權 鑒權可以說是身份驗證,身份驗證是確定用戶身份的過程; 在ASP.NET Core 中身份驗證是由身份驗證服務IAuthenticationService負責的,它被身份驗證中間件使用, 身份驗證服務會 ...


.net core 自定義授權策略提供程式進行許可權驗證

在這之前先瞭解一下鑒權和授權的概念;

鑒權

鑒權可以說是身份驗證,身份驗證是確定用戶身份的過程;

在ASP.NET Core 中身份驗證是由身份驗證服務IAuthenticationService負責的,它被身份驗證中間件使用, 身份驗證服務會使用已註冊的身份驗證處理程式來完成與身份驗證相關的操作。身份驗證相關的操作包括:對用戶身份進行驗證,對未經身份驗證的用戶進行資源訪問時做出響應。

身份驗證處理程式及其配置選項

身份驗證處理程式包括CookieAuthenticationHandler 和 JwtBearerHandler,身份驗證處理程式的註冊 是在調用AddAuthentication之後擴展方法AddJwtBearer 和 AddCookie 提供的

身份驗證處理程式會由實現IAuthenticationService 介面的AuthenticationService 的AuthenticateAsync 方法去調用

授權

授權是確定用戶是否有權訪問資源的過程,這裡先簡單帶過一下後面接著講

授權方案

授權方案包括 基於角色的授權,基於聲明的授權,基於策略的授權,這裡著重說一下策略授權,

基於策略的授權

授權策略包含一個或多個要求

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AtLeast21", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(21)));
});

在前面的示例中,創建了“AtLeast21”策略。 該策略有一個最低年齡要求,其作為要求的參數提供

IAuthorizationRequirement

IAuthorizationRequirement用於跟蹤授權是否成功的機制

在IAuthorizationHandler 的 HandleAsync方法中 作為參數被調用,由HttpContext 的Requirements 屬性提供

IAuthorizationHandler

IAuthorizationHandler 用於檢查策略是否滿足要求,主要執行的方法是HandleAsync,我們可以繼承微軟提供的AuthorizationHandler,預設實現了HandlAsync ,在具有多個IAuthorizationRequirement 的情況下預設是迴圈去執行HandleRequirementAsync方法,在某些情況下我們可以去重寫從而去執行特定IAuthorizationRequirement,當然方法多樣

        public virtual async Task HandleAsync(AuthorizationHandlerContext context)
        {
            if (context.Resource is TResource)
            {
                foreach (var req in context.Requirements.OfType<TRequirement>())
                {
                    await HandleRequirementAsync(context, req, (TResource)context.Resource);
                }
            }
        }
IAuthorizationPolicyProvider

IAuthorizationPolicyProvider 自定義策略提供程式

繼承IAuthorizationPolicyProvider 需要去實現IAuthorizationPolicyProvider 三個方法,三個方法的執行由我們我們的Authorize特性決定,並且Authorize特性的策略名稱會傳遞到 IAuthorizationPolicyProvider 的GetPolicyAsync 作為參數使用

        public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
        {
            var policy = new AuthorizationPolicyBuilder();
            //添加鑒權方案
            policy.AddAuthenticationSchemes("Bearer");
            policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
            if (policyName is null)
            {
                return Task.FromResult<AuthorizationPolicy>(null);
            }
            var authorizations = policyName.Split(',');
            if (authorizations.Any())
            {
                //許可權策略構建器,添加自定義的AuthorizaRequirement
                policy.AddRequirements(new AuthorizeRequirement(authorizations));
            }
            return Task.FromResult(policy.Build());
        }

這裡看一下Authorize特性相關的屬性

    public class AuthorizeAttribute : Attribute, IAuthorizeData
    {
        /// <summary>
        /// </summary>
        public AuthorizeAttribute() { }

        /// <summary>
        /// 初始化類類實例並且設置可訪問資源策略名稱
        /// </summary>
        /// <param name="policy">The name of the policy to require for authorization.</param>
        public AuthorizeAttribute(string policy)
        {
            Policy = policy;
        }
        /// <summary>
        /// 設置或獲取可以訪問資源策略名稱
        /// </summary>
        public string? Policy { get; set; }

        /// <summary>
        /// 設置或獲取可以訪問資源的角色
        /// </summary>
        public string? Roles { get; set; }

        /// <summary>
        /// 設置或獲取可以訪問資源鑒權方案名稱
        /// </summary>
        public string? AuthenticationSchemes { get; set; }
    }
GetDefaultPolicyAsync

預設策略,當我們的Authorize特性上不提供策略時執行,這裡的CustomAuthorization特性是繼承了Authorize特性

        [CustomAuthorization]
        [HttpGet("SetNotOP")]
        [NoResult]
        public int SetNotOP()
        {
            throw new ArgumentNullException(nameof(TestTask));
            return 1;
        }
GetPolicyAsync

在Authorize添加策略時執行

        [CustomAuthorization("test1", "test1")]
        [HttpGet("TestTask")]
        public async Task<int> TestTask()
        {
            await Task.CompletedTask;
            return 1;
        }
GetFallbackPolicyAsync

後備授權策略,是指在沒有為請求指定其他策略時,由授權中間件提供的策略。在這裡可以指返回空值,也可以設置指定策略返回

    public Task<AuthorizationPolicy?> GetFallbackPolicyAsync()
    {
        return Task.FromResult<AuthorizationPolicy>(null);
    }
    //或者
    public Task<AuthorizationPolicy?> GetFallbackPolicyAsync()
    {
        var policy = new AuthorizationPolicyBuilder();
        policy.AddAuthenticationSchemes("Bearer");
        policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
        return Task.FromResult<AuthorizationPolicy>(policy.Build());
    }
IAuthorizationService

IAuthorizationService是確認授權成功與否的主要服務,兵器 負責去執行我們自定義的AuthorizationHandle

看一段由微軟官方簡化授權服務的代碼,可以看到AuthorizeAsync會去迴圈執行自定義的AuthorizationHandle

public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, 
             object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
    // Create a tracking context from the authorization inputs.
    var authContext = _contextFactory.CreateContext(requirements, user, resource);

    // By default this returns an IEnumerable<IAuthorizationHandlers> from DI.
    var handlers = await _handlers.GetHandlersAsync(authContext);

    // Invoke all handlers.
    foreach (var handler in handlers)
    {
        await handler.HandleAsync(authContext);
    }

    // Check the context, by default success is when all requirements have been met.
    return _evaluator.Evaluate(authContext);
}

到這裡應該對整個授權流程有了個大致的瞭解,在授權前會由鑒權中間件進行一個鑒權,鑒權通過後由IAuthorizationPolicyProvider 來提供一個授權策略(授權策略里可以添加我們需要的IAuthorizationRequirement),最後由IAuthorizationService 的HandleAsync去執行自定義AuthorizeHandle

具體實現

自定義特性
    public class CustomAuthorizationAttribute : AuthorizeAttribute
    {
        public virtual string[] AuthorizeName { get; private set; }

        public CustomAuthorizationAttribute(params string[] authorizeName)
        {
            AuthorizeName = authorizeName;
            Policy = string.Join(",", AuthorizeName);
        }
    }
自定義策略提供程式
    public class AuthorizationProvider : IAuthorizationPolicyProvider
    {
        public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
        {
            var policy = new AuthorizationPolicyBuilder();
            policy.AddAuthenticationSchemes("Bearer");
            policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
            //預設授權測率必須添加一個IAuthorizationRequirement的實現
            policy.AddRequirements(new AuthorizeRequirement());
            return Task.FromResult<AuthorizationPolicy>(policy.Build());
        }

        public Task<AuthorizationPolicy?> GetFallbackPolicyAsync()
        {
            return Task.FromResult<AuthorizationPolicy>(null);
        }

        public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
        {
            var policy = new AuthorizationPolicyBuilder();
            policy.AddAuthenticationSchemes("Bearer");
            policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
            if (policyName is null)
            {
                return Task.FromResult<AuthorizationPolicy>(null);
            }
            var authorizations = policyName.Split(',');
            if (authorizations.Any())
            {
                policy.AddRequirements(new AuthorizeRequirement(authorizations));
            }
            return Task.FromResult(policy.Build());
        }
    }
自定義授權處理程式

IPermissionsCheck 是我註入的許可權檢測程式,其實對於許可權認證,重要的是控制對資源的訪問,整篇文章下來無非就是將特性上的值提供到我們所需要進行許可權檢測的程式中去,當然我們也可以用許可權過濾器反射獲取Authorize特性上的值來實現

public class AuthorizeHandler : AuthorizationHandler<AuthorizeRequirement>
{
    private readonly IPermissionCheck _permisscheck;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuthorizeHandler(IHttpContextAccessor httpContextAccessor
        , IServiceProvider serviceProvider)
    {
        using var scope = serviceProvider.CreateScope();
        _permisscheck = scope.ServiceProvider.GetRequiredService<IPermissionCheck>();
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizeRequirement requirement)
    {
        var identity = _httpContextAccessor?.HttpContext?.User?.Identity;
        var httpContext = _httpContextAccessor?.HttpContext;
        var isAuthenticated = identity?.IsAuthenticated ?? false;
        var claims = _httpContextAccessor?.HttpContext?.User?.Claims;
        var userId = claims?.FirstOrDefault(p => p.Type == "Id")?.Value;
        //判斷是否通過鑒權中間件--是否登錄
        if (userId is null || !isAuthenticated)
        {
            context.Fail();
            return;
        }
        var defaultPolicy = requirement.AuthorizeName?.Any() ?? false;
        //預設授權策略
        if (!defaultPolicy)
        {
            context.Succeed(requirement);
            return;
        }
        var roleIds = claims?
            .Where(p => p?.Type?.Equals("RoleIds") ?? false)
            .Select(p => long.Parse(p.Value));
        var roleNames = claims?
            .Where(p => p?.Type?.Equals(ClaimTypes.Role) ?? false)
            .Select(p => p.Value);
        UserTokenModel tokenModel = new UserTokenModel()
        {
            UserId = long.Parse(userId ?? "0"),
            UserName = claims?.FirstOrDefault(p => p.Type == ClaimTypes.Name)?.Value ?? "",
            RoleNames = roleNames?.ToArray(),
            RoleIds = roleIds?.ToArray(),
        };
        if (requirement.AuthorizeName.Any())
        {
            if (!_permisscheck.IsGranted(tokenModel, requirement.AuthorizeName))
            {
                context.Fail();
                return;
            }
        }
        context.Succeed(requirement);
    }
}
自定義IAuthorizationRequirement
public class AuthorizeRequirement : IAuthorizationRequirement
{
    public virtual string[] AuthorizeName { get; private set; }
    public AuthorizeRequirement(params string[] authorizeName)
    {
        AuthorizeName = authorizeName;
    }

    public AuthorizeRequirement() { }
}
自定義授權結果中間件

自定義授權結果中間件的作用,返回自定義響應,增強預設質詢或禁止響應

    public class AuthorizeMiddleHandle : IAuthorizationMiddlewareResultHandler
    {
        public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy                                               policy, PolicyAuthorizationResult authorizeResult)
        {
            if (!authorizeResult.Succeeded || authorizeResult.Challenged)
            {
                var isLogin = context?.User?.Identity?.IsAuthenticated ?? false;
                var path = context?.Request?.Path ?? "";
                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                var response = new AjaxResponse();
                response.UnAuthorizedRequest = true;
                response.StatusCode = "401";
                var error = new ErrorInfo();
                error.Error = isLogin ? $"你沒有許可權訪問該介面-介面路由{path}" : "請先登錄系統";
                response.Error = error;
                await context.Response.WriteAsJsonAsync(response);
                return;
            }
            await next(context);
        }
    }
相關服務的註冊
            context.Services.AddScoped<IPermissionCheck, PermissionCheck>();
            context.Services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationProvider>();
            context.Services.AddSingleton<IAuthorizationMiddlewareResultHandler, AuthorizeMiddleHandle>();
            context.Services.AddSingleton<IAuthorizationHandler, AuthorizeHandler>();

到這裡對於許可權認證有了個大概的瞭解,至於是通過自定義策略提供程式自定義AuthorizHandle這一系列複雜的操作還是通過許可權過濾器取決看官自己。個人認為通過自定義策略提供程式自定義AuthorizHandle這種方式更靈活性,能夠應對更多複雜場景。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在java打演算法題的時候,Scanner類、Sout的速度太慢,所以要用PrintWriter和BufferReader&StreamTokenizer類來進行快速輸入。代碼如下: import java.io.*; public class Main { public static PrintWr ...
  • 記憶體重疊是指在記憶體中存在兩個或多個區域,它們的地址範圍有交叉部分。在 C++ 中,記憶體重疊可能會導致程式出現不可預期的行為,因此我們需要瞭解它的原因和如何避免。 記憶體重疊的原因 記憶體重疊的主要原因是指針的使用。當我們使用指針訪問記憶體時,如果指針指向的記憶體區域與另一個區域有交叉部分,就會產生記憶體重疊。 ...
  • Mybatis高級特性能夠幫助我們更加靈活地操作資料庫,包括動態SQL、緩存機制、插件機制、自定義類型轉換等。學習這些特性可以讓我們更好地利用Mybatis,提高數據操作的效率和質量。 未來的道路由自己抉擇,事業的高度由自己決定。 動態SQL 動態SQL中,Mybatis提供了多種標簽來幫助我們構建 ...
  • 一、問題引入 在學習棧的過程中,教材有一個案例:利用棧結果解析括弧的匹配問題。括弧問題:[({}{})],說明 [] 、() 、{} 稱為一對。 號碼位置對應的括弧之間進行匹配,結果:0-7、 1-6、 2-3、 4-5 二、過程記錄 💡 基於順序棧實現 利用棧的特性:先進後出 ,對括弧進行匹配輸 ...
  • 本系列將和大家分享Redis分散式緩存,本章主要簡單介紹下Redis中的布隆過濾器(Bloom Filter),以及如何破解ServiceStack和如何解決緩存雪崩、緩存穿透、緩存擊穿、緩存預熱問題。 ...
  • 一、Show與ShowDialog眾所周知在c#中有兩種顯示視窗的方式:模態顯示(showdialog)與非模態顯示(show),模態顯示會阻塞調用視窗的所有消息響應,在調用ShowDialog方法後,直到關閉對話框後,才執行此方法後面的代碼 ,期間用戶是無法對該視窗外的界面進行ui交互的;非模態顯 ...
  • 背景: 如何在ASP.Net Core的生產環境中保護swagger ui,也就是index.html頁面。其實swagger是自帶禁用的功能的,只需要設置開關即可。但是有一些場景,是需要把這些介面進行開放或者導出成文檔供第三方進行調用,這個時候卻又不想讓所有人訪問。本文介紹一種許可權控制訪問的方式, ...
  • 一:背景 1. 講故事 最近收到了兩起程式崩潰的dump,查了下都是經典的 double free 造成的,蠻有意思,這裡就抽一篇出來分享一下經驗供後面的學習者避坑吧。 二:WinDbg 分析 1. 崩潰點在哪裡 windbg 帶了一個自動化分析命令 !analyze -v 可以幫助我們找到崩潰時的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...