.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
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...