溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

ASP.NET?Core中策略授權(quán)和ABP授權(quán)怎么用

發(fā)布時間:2022-02-23 11:45:47 來源:億速云 閱讀:162 作者:小新 欄目:開發(fā)技術(shù)

小編給大家分享一下ASP.NET Core中策略授權(quán)和ABP授權(quán)怎么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

ASP.NET Core 中的策略授權(quán)

首先我們來創(chuàng)建一個 WebAPI 應(yīng)用。

然后引入 Microsoft.AspNetCore.Authentication.JwtBearer 包。

策略

Startup 類的 ConfigureServices 方法中,添加一個策略的形式如下:

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

這里我們分步來說。

services.AddAuthorization 用于添加授權(quán)方式,目前只支持 AddPolicy。

ASP.NET Core 中,有基于角色、聲明、策略的三種授權(quán)形式,都是使用 AddPolicy 來添加授權(quán)處理。

其中,有兩個 API 如下:

        public void AddPolicy(string name, AuthorizationPolicy policy);
        public void AddPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy);

name = "AtLeast21",這里 "AtLeast21" 是策略的名稱。

policy.Requirements.Add() 用于添加一個策略的標(biāo)記(存儲此策略的數(shù)據(jù)),此標(biāo)記需要繼承 IAuthorizationRequirement 接口。

策略的名稱應(yīng)該如何設(shè)置呢?在授權(quán)上應(yīng)該如何編寫策略以及使用 Requirements.Add()?

這里先放一放,我們接下來再講解。

定義一個 Controller

我們來添加一個 Controller :

    [ApiController]
    [Route("[controller]")]
    public class BookController : ControllerBase
    {
        private static List<string> BookContent = new List<string>();
        [HttpGet("Add")]
        public string AddContent(string body)
        {
            BookContent.Add(body);
            return "success";
        }

        [HttpGet("Remove")]
        public string RemoveContent(int n)
        {
            BookContent.Remove(BookContent[n]);
            return "success";
        }

        [HttpGet("Select")]
        public List<object> SelectContent()
        {
            List<object> obj = new List<object>();
            int i = 0;
            foreach (var item in BookContent)
            {
                int tmp = i;
                i++;
                obj.Add(new { Num = tmp, Body = item });
            }
            return obj;
        }

        [HttpGet("Update")]
        public string UpdateContent(int n, string body)
        {
            BookContent[n] = body;
            return "success";
        }
    }

功能很簡單,就是對列表內(nèi)容增刪查改。

設(shè)定權(quán)限

前面我們創(chuàng)建了 BookController ,具有增刪查改的功能。應(yīng)該為每一個功能都應(yīng)該設(shè)置一種權(quán)限。

ASP.NET Core 中,一個權(quán)限標(biāo)記,需要繼承IAuthorizationRequirement 接口。

我們來設(shè)置五個權(quán)限:

添加一個文件,填寫以下代碼。

    /*
     IAuthorizationRequirement 是一個空接口,具體對于授權(quán)的需求,其屬性等信息是自定義的
     這里的繼承關(guān)系也沒有任何意義
     */

    // 能夠訪問 Book 的權(quán)限
    public class BookRequirment : IAuthorizationRequirement
    {
    }

    // 增刪查改 Book 權(quán)限
    // 可以繼承 IAuthorizationRequirement ,也可以繼承 BookRequirment
    public class BookAddRequirment : BookRequirment
    {
    }
    public class BookRemoveRequirment : BookRequirment
    {
    }
    public class BookSelectRequirment : BookRequirment
    {
    }
    public class BookUpdateRequirment : BookRequirment
    {
    }

BookRequirment 代表能夠訪問 BookController,其它四個分別代表增刪查改的權(quán)限。

定義策略

權(quán)限設(shè)定后,我們開始設(shè)置策略。

在 Startup 的 ConfigureServices 中,添加:

            services.AddAuthorization(options =>
            {
                options.AddPolicy("Book", policy =>
                {
                    policy.Requirements.Add(new BookRequirment());
                });

                options.AddPolicy("Book:Add", policy =>
                {
                    policy.Requirements.Add(new BookAddRequirment());
                });

                options.AddPolicy("Book:Remove", policy =>
                {
                    policy.Requirements.Add(new BookRemoveRequirment());
                });

                options.AddPolicy("Book:Select", policy =>
                {
                    policy.Requirements.Add(new BookSelectRequirment());
                });

                options.AddPolicy("Book:Update", policy =>
                {
                    policy.Requirements.Add(new BookUpdateRequirment());
                });

            });

這里我們?yōu)槊糠N策略只設(shè)置一種權(quán)限,當(dāng)然每種策略都可以添加多個權(quán)限,

這里名稱使用 : 隔開,主要是為了可讀性,讓人一看就知道是層次關(guān)系。

存儲用戶信息

這里為了更加簡單,就不使用數(shù)據(jù)庫了。

以下用戶信息結(jié)構(gòu)是隨便寫的。用戶-角色-角色具有的權(quán)限。

這個權(quán)限用什么類型存儲都可以。只要能夠標(biāo)識區(qū)分是哪個權(quán)限就行。

    /// <summary>
    /// 存儲用戶信息
    /// </summary>
    public static class UsersData
    {
        public static readonly List<User> Users = new List<User>();
        static UsersData()
        {
            // 添加一個管理員
            Users.Add(new User
            {
                Name = "admin",
                Email = "admin@admin.com",
                Role = new Role
                {
                    Requirements = new List<Type>
                    {
                        typeof( BookRequirment),
                        typeof( BookAddRequirment),
                        typeof( BookRemoveRequirment),
                        typeof( BookSelectRequirment),
                        typeof( BookUpdateRequirment)
                    }
                }
            });

            // 沒有刪除權(quán)限
            Users.Add(new User
            {
                Name = "作者",
                Email = "wirter",
                Role = new Role
                {
                    Requirements = new List<Type>
                    {
                        typeof( BookRequirment),
                        typeof( BookAddRequirment),
                        typeof( BookRemoveRequirment),
                        typeof( BookSelectRequirment),
                    }
                }
            });
        }
    }

    public class User
    {
        public string Name { get; set; }
        public string Email { get; set; }
        public Role Role { get; set; }
    }

    /// <summary>
    /// 這里的存儲角色的策略授權(quán),字符串?dāng)?shù)字等都行,只要能夠存儲表示就OK
    /// <para>在這里沒有任何意義,只是標(biāo)識的一種方式</param>
    /// </summary>
    public class Role
    {
        public List<Type> Requirements { get; set; }
    }

標(biāo)記訪問權(quán)限

定義策略完畢后,就要為 Controller 和 Action 標(biāo)記訪問權(quán)限了。

使用 [Authorize(Policy = "{string}")] 特性和屬性來設(shè)置訪問此 Controller 、 Action 所需要的權(quán)限。

這里我們分開設(shè)置,每個功能標(biāo)記一種權(quán)限(最小粒度應(yīng)該是一個功能 ,而不是一個 API)。

    [Authorize(Policy = "Book")]
    [ApiController]
    [Route("[controller]")]
    public class BookController : ControllerBase
    {
        private static List<string> BookContent = new List<string>();

        [Authorize(Policy = "Book:Add")]
        [HttpGet("Add")]
        public string AddContent(string body){}

        [Authorize(Policy = "Book:Remove")]
        [HttpGet("Remove")]
        public string RemoveContent(int n){}

        [Authorize(Policy = "Book:Select")]
        [HttpGet("Select")]
        public List<object> SelectContent(){}

        [Authorize(Policy = "Book:Update")]
        [HttpGet("Update")]
        public string UpdateContent(int n, string body){}
    }

認(rèn)證:Token 憑據(jù)

因為使用的是 WebAPI,所以使用 Bearer Token 認(rèn)證,當(dāng)然使用 Cookie 等也可以。使用什么認(rèn)證方式都可以。

            // 設(shè)置驗證方式為 Bearer Token
            // 添加 using Microsoft.AspNetCore.Authentication.JwtBearer;
            // 你也可以使用 字符串 "Brearer" 代替 JwtBearerDefaults.AuthenticationScheme
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdABCD1234abcdABCD1234")),    // 加密解密Token的密鑰

                        // 是否驗證發(fā)布者
                        ValidateIssuer = true,
                        // 發(fā)布者名稱
                        ValidIssuer = "server",

                        // 是否驗證訂閱者
                        // 訂閱者名稱
                        ValidateAudience = true,
                        ValidAudience = "client007",

                        // 是否驗證令牌有效期
                        ValidateLifetime = true,
                        // 每次頒發(fā)令牌,令牌有效時間
                        ClockSkew = TimeSpan.FromMinutes(120)
                    };
                });

上面的代碼是一個模板,可以隨便改。這里的認(rèn)證方式跟我們的策略授權(quán)沒什么關(guān)系。

頒發(fā)登錄憑據(jù)

下面這個 Action 放置到 BookController,作為登錄功能。這一部分也不重要,主要是為用戶頒發(fā)憑據(jù),以及標(biāo)識用戶。用戶的 Claim 可以存儲此用戶的唯一標(biāo)識。

        /// <summary>
        /// 用戶登錄并且頒發(fā)憑據(jù)
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpGet("Token")]
        public string Token(string name)
        {
            User user = UsersData.Users.FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
            if (user is null)
                return "未找到此用戶";

            // 定義用戶信息
            var claims = new Claim[]
            {
                new Claim(ClaimTypes.Name, name),
                new Claim(JwtRegisteredClaimNames.Email, user.Email)
            };

            // 和 Startup 中的配置一致
            SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdABCD1234abcdABCD1234"));

            JwtSecurityToken token = new JwtSecurityToken(
                issuer: "server",
                audience: "client007",
                claims: claims,
                notBefore: DateTime.Now,
                expires: DateTime.Now.AddMinutes(30),
                signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
            );

            string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
            return jwtToken;
        }

Configure 中補充以下兩行:

            app.UseAuthentication();
            app.UseAuthorization();

自定義授權(quán)

自定義授權(quán)需要繼承 IAuthorizationHandler 接口,實現(xiàn)此接口的類能夠決定是否對用戶的訪問進行授權(quán)。

實現(xiàn)代碼如下:

    /// <summary>
    /// 判斷用戶是否具有權(quán)限
    /// </summary>
    public class PermissionHandler : IAuthorizationHandler
    {
        public async Task HandleAsync(AuthorizationHandlerContext context)
        {
            // 當(dāng)前訪問 Controller/Action 所需要的權(quán)限(策略授權(quán))
            IAuthorizationRequirement[] pendingRequirements = context.PendingRequirements.ToArray();

            // 取出用戶信息
            IEnumerable<Claim> claims = context.User?.Claims;

            // 未登錄或者取不到用戶信息
            if (claims is null)
            {
                context.Fail();
                return;
            }


            // 取出用戶名
            Claim userName = claims.FirstOrDefault(x => x.Type == ClaimTypes.Name);
            if (userName is null)
            {
                context.Fail();
                return;
            }
            // ... 省略一些檢驗過程 ...

            // 獲取此用戶的信息
            User user = UsersData.Users.FirstOrDefault(x => x.Name.Equals(userName.Value, StringComparison.OrdinalIgnoreCase));
            List<Type> auths = user.Role.Requirements;

            // 逐個檢查
            foreach (IAuthorizationRequirement requirement in pendingRequirements)
            {
                // 如果用戶權(quán)限列表中沒有找到此權(quán)限的話
                if (!auths.Any(x => x == requirement.GetType()))
                    context.Fail();

                context.Succeed(requirement);
            }

            await Task.CompletedTask;
        }
    }

過程:

  • 從上下文(Context) 中獲取用戶信息(context.User)

  • 獲取此用戶所屬的角色,并獲取此角色具有的權(quán)限

  • 獲取此次請求的 Controller/Action 需要的權(quán)限(context.PendingRequirements)

  • 檢查所需要的權(quán)限(foreach循環(huán)),此用戶是否都具有

最后需要將此接口、服務(wù),注冊到容器中:

services.AddSingleton<IAuthorizationHandler, PermissionHandler>();

做完這些后,就可以測試授權(quán)了。

IAuthorizationService

前面實現(xiàn)了 IAuthorizationHandler 接口的類,用于自定義確定用戶是否有權(quán)訪問此 Controller/Action。

IAuthorizationService 接口用于確定授權(quán)是否成功,其定義如下:

public interface IAuthorizationService
    {
        Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object? resource, IEnumerable<IAuthorizationRequirement> requirements);

        Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object? resource, string policyName);
    }

DefaultAuthorizationService 接口實現(xiàn)了 IAuthorizationService ,ASP.NET Core 默認(rèn)使用 DefaultAuthorizationService 來確認(rèn)授權(quán)。

前面我們使用 IAuthorizationHandler 接口來自定義授權(quán),如果再深入一層的話,就追溯到了IAuthorizationService

DefaultAuthorizationService 是 IAuthorizationService 的默認(rèn)實現(xiàn),其中有一段代碼如下:

ASP.NET?Core中策略授權(quán)和ABP授權(quán)怎么用

DefaultAuthorizationService 比較復(fù)雜,一般情況下,我們只要實現(xiàn) IAuthorizationHandler 就夠了。

參考資料:https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.aspnetcore.authorization.defaultauthorizationservice?view=aspnetcore-3.1

ABP 授權(quán)

前面已經(jīng)介紹了 ASP.NET Core 中的策略授權(quán),這里介紹一下 ABP 中的授權(quán),我們繼續(xù)利用前面已經(jīng)實現(xiàn)的 ASP.NET Core 代碼。

創(chuàng)建 ABP 應(yīng)用

Nuget 安裝 Volo.Abp.AspNetCore.Mvc、Volo.Abp.Autofac 。

創(chuàng)建 AppModule 類,代碼如下:

    [DependsOn(typeof(AbpAspNetCoreMvcModule))]
    [DependsOn(typeof(AbpAutofacModule))]
    public class AppModule : AbpModule
    {
        public override void OnApplicationInitialization(
            ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            app.UseStaticFiles();
            app.UseRouting();
            app.UseConfiguredEndpoints();
        }
    }

在 Program 的 Host 加上 .UseServiceProviderFactory(new AutofacServiceProviderFactory()),示例如下:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .UseServiceProviderFactory(new AutofacServiceProviderFactory())
            ...
            ...

然后在 Startup 中的 ConfiguraServices 方法中,添加 ABP 模塊, 并且設(shè)置使用 Autofac。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddApplication<AppModule>(options=>
            {
                options.UseAutofac();
            });
        }

定義權(quán)限

ABP 中使用 PermissionDefinitionProvider 類來定義權(quán)限,創(chuàng)建一個類,其代碼如下:

    public class BookPermissionDefinitionProvider : PermissionDefinitionProvider
    {
        public override void Define(IPermissionDefinitionContext context)
        {
            var myGroup = context.AddGroup("Book");
            var permission = myGroup.AddPermission("Book");
            permission.AddChild("Book:Add");
            permission.AddChild("Book:Remove");
            permission.AddChild("Book:Select");
            permission.AddChild("Book:Update");
        }
    }

這里定義了一個組 Book,定義了一個權(quán)限 Book了,Book 其下有四個子權(quán)限。

刪除 Startup 中的services.AddAuthorization(options =>... 。

將剩余的依賴注入服務(wù)代碼移動到 AppModule 的 ConfigureServices 中。

Startup 的 Configure 改成:

            app.InitializeApplication();

AbpModule 中的 Configure 改成:

            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            app.UseStaticFiles();
            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseConfiguredEndpoints();

PermissionHandler 需要改成:

    public class PermissionHandler : IAuthorizationHandler
    {
        public Task HandleAsync(AuthorizationHandlerContext context)
        {
            // 當(dāng)前訪問 Controller/Action 所需要的權(quán)限(策略授權(quán))
            IAuthorizationRequirement[] pendingRequirements = context.PendingRequirements.ToArray();

            // 逐個檢查
            foreach (IAuthorizationRequirement requirement in pendingRequirements)
            {
                context.Succeed(requirement);
            }


            return Task.CompletedTask;
        }
    }

刪除 UserData 文件;BookController 需要修改一下登錄和憑證。

以上是“ASP.NET Core中策略授權(quán)和ABP授權(quán)怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI