一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - ASP.NET教程 - ASP.NET Core 6框架揭秘實例演示[40]:基于角色的授權

ASP.NET Core 6框架揭秘實例演示[40]:基于角色的授權

2023-08-12 00:05未知服務器之家 ASP.NET教程

ASP.NET應用并沒有對如何定義授權策略做硬性規定,但是針對角色的授權策略依然是最常用的。角色(或者用戶組)實際上就是對一組權限集的描述,將一個用戶添加到某個角色之中就是為了將對應的權限賦予該用戶。在《使用最簡

ASP.NET Core 6框架揭秘實例演示[40]:基于角色的授權ASP.NET應用并沒有對如何定義授權策略做硬性規定,但是針對角色的授權策略依然是最常用的。角色(或者用戶組)實際上就是對一組權限集的描述,將一個用戶添加到某個角色之中就是為了將對應的權限賦予該用戶。在《使用最簡潔的代碼實現登錄、認證和注銷》中,我們提供了一個用來演示登錄、認證和注銷的程序,現在我們在此基礎上添加基于“角色授權的部分”。

ASP.NET應用并沒有對如何定義授權策略做硬性規定,所以我們完全根據用戶具有的任意特性(如性別、年齡、學歷、所在地區、宗教信仰、政治面貌等)來判斷其是否具有獲取目標資源或者執行目標操作的權限,但是針對角色的授權策略依然是最常用的。角色(或者用戶組)實際上就是對一組權限集的描述,將一個用戶添加到某個角色之中就是為了將對應的權限賦予該用戶。在《使用最簡潔的代碼實現登錄、認證和注銷》中,我們提供了一個用來演示登錄、認證和注銷的程序,現在我們在此基礎上添加基于“角色授權的部分”。(本文提供的示例演示已經同步到《ASP.NET Core 6框架揭秘-實例演示版》)

[S2801]基于“要求”的授權
[S2802]基于“策略”的授權
[S2803]將“角色”綁定到路由終結點
[S2804]將“授權策略”綁定到路由終結點

[S2801]基于“要求”的授權

我們提供的演示實例提供了IAccountService和IPageRenderer兩個服務,前者用用來進行校驗密鑰,后者用來呈現主頁和登錄頁面。為了在認證的時候一并將用戶擁有的角色提取出來,我們按照如下的方式為IAccountService接口的Validate方法添加了表示角色列表的輸出參數。對于實現類AccountService提供的三個賬號來說,只有“Bar”擁有一個名為“Admin”的角色。

public interface IAccountService
{
    bool Validate(string userName, string password, out string[] roles);
}

public class AccountService : IAccountService
{
    private readonly Dictionary<string, string> _accounts = new(StringComparer.OrdinalIgnoreCase)
    {
        { "Foo", "password" },
        { "Bar", "password" },
        { "Baz", "password" }
    };

    private readonly Dictionary<string, string[]> _roles = new(StringComparer.OrdinalIgnoreCase)
    {
            { "Bar", new string[]{"Admin" } }
    };

    public bool Validate(string userName, string password, out string[] roles)
    {
        if (_accounts.TryGetValue(userName, out var pwd) && pwd == password)
        {
            roles = _roles.TryGetValue(userName, out var value) ? value : Array.Empty<string>();
            return true;
        }
        roles = Array.Empty<string>();
        return false;
    }
}

我們假設演示的應用是供擁有“Admin”角色的管理人員使用的,所以只能擁有該角色的用戶才能訪問應用的主頁,未授權訪問會自動定向到我們提供的“訪問拒絕”頁面。我們在另一個IPageRenderer服務接口中添加了如下這個RenderAccessDeniedPage方法,并在PageRenderer類型中完成了對應的實現。

public interface IPageRenderer
{
    IResult RenderLoginPage(string? userName = null, string? password = null, string? errorMessage = null);
    IResult RenderAccessDeniedPage(string userName);
    IResult RenderHomePage(string userName);
}

public class PageRenderer : IPageRenderer
{
    public IResult RenderAccessDeniedPage(string userName)
    {
        var html = @$"
<html>
    <head><title>Index</title></head>
    <body>
        <h3>{userName}, your access is denied.</h3>
        <a href='/Account/Logout'>Change another account</a>
    </body>
</html>";
        return Results.Content(html, "text/html");
    }
    ...
}

在現有的演示程序基礎上,我們不需要作太大的修改。由于需要引用授權功能,我們調用了IServiceCollection接口的AddAuthorization擴展方法注冊了必要的服務。由于引入了“訪問決絕”頁面,我們注冊了對應的終結點,該終結點依然采用標準的路徑“Account/AccessDenied”,對應的處理方法DenyAccess直接調用上面這個RenderAccessDeniedPage方法將該頁面呈現出來。

using App;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using System.Security.Claims;
using System.Security.Principal;

var builder = WebApplication.CreateBuilder();
builder.Services
    .AddSingleton<IPageRenderer, PageRenderer>()
    .AddSingleton<IAccountService, AccountService>()
    .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();

app.Map("/", WelcomeAsync);
app.MapGet("Account/Login", Login);
app.MapPost("Account/Login", SignInAsync);
app.Map("Account/Logout", SignOutAsync);
app.Map("Account/AccessDenied", DenyAccess);

app.Run();

Task WelcomeAsync(HttpContext context, ClaimsPrincipal user, IPageRenderer renderer, IAuthorizationService authorizationService);
IResult Login(IPageRenderer renderer);
Task SignInAsync(HttpContext context, HttpRequest request, IPageRenderer renderer,IAccountService accountService);
Task SignOutAsync(HttpContext context);
IResult DenyAccess(ClaimsPrincipal user, IPageRenderer renderer) => renderer.RenderAccessDeniedPage(user?.Identity?.Name!);
我們需要對用來認證請求的SignInAsync方法作相應的修改。如下的代碼片段所示,對于成功通過認證的用戶,我們會為它創建一個ClaimsPrincipal對象來表示當前用戶。這個對象也是授權的目標對象,授權的本質就是確定該對象是否攜帶了授權資源或者操作所要求的“資質”。由于我們采用的是基于“角色”的授權,所以我們將該用于擁有的角色以“聲明(Claim)”的形式添加到表示身份的ClaimsIdentity對象上。
Task SignInAsync(HttpContext context, HttpRequest request, IPageRenderer renderer,IAccountService accountService)
{
    var username = request.Form["username"];
    if (string.IsNullOrEmpty(username))
    {
        return renderer.RenderLoginPage(null, null, "Please enter user name.").ExecuteAsync(context);
    }

    var password = request.Form["password"];
    if (string.IsNullOrEmpty(password))
    {
        return renderer.RenderLoginPage(username, null, "Please enter user password.").ExecuteAsync(context);
    }

    if (!accountService.Validate(username, password, out var roles))
    {
        return renderer.RenderLoginPage(username, null, "Invalid user name or password.").ExecuteAsync(context);
    }

    var identity = new GenericIdentity(name: username, type: CookieAuthenticationDefaults.AuthenticationScheme);
    foreach (var role in roles)
    {
        identity.AddClaim(new Claim(ClaimTypes.Role, role));
    }
    var user = new ClaimsPrincipal(identity);
    return context.SignInAsync(user);
}

演示實例授權的效果就是讓擁有“Admin”角色的用戶才能訪問主頁,所以我們將授權實現在如下這個WelcomeAsync方法中。如果當前用戶(由注入的ClaimsPrincipal對象表示)并未通過認證,我們依然調用HttpContext上下文的ChallengeAsync擴展方法返回一個“匿名請求”的質詢。在確定用戶通過認證的前提下,我們創建了一個RolesAuthorizationRequirement來表示主頁針對授權用戶的“角色要求”。授權檢驗通過調用注入的IAuthorizationService對象的AuthorizeAsync方法來完成,我們將代表當前用戶的ClaimsPrincipal對象和包含RolesAuthorizationRequirement對象的數組作為參數。如果授權成功,主頁得以正常呈現,否則我們調用HttpContext上下文的ForbidAsync擴展方法返回“權限不足”的質詢,上面提供的“拒絕訪問”頁面將會呈現出來。

async Task WelcomeAsync(HttpContext context, ClaimsPrincipal user, IPageRenderer renderer,IAuthorizationService authorizationService)
{
    if (user?.Identity?.IsAuthenticated ?? false)
    {
        var requirement = new RolesAuthorizationRequirement(new string[] { "admin" });
        var result = await authorizationService.AuthorizeAsync(
            user:user, resource: null,
            requirements: new IAuthorizationRequirement[] { requirement });
        if (result.Succeeded)
        {
            await renderer.RenderHomePage(user.Identity.Name!).ExecuteAsync(context);
        }
        else
        {
            await context.ForbidAsync();
        }
    }
    else
    {
      await  context.ChallengeAsync();
    }
}

程序啟動之后,具有“Admin”權限的“Bar”用戶能夠正常主頁,其他的用戶(比如“Foo”)會自動重定向到“訪問拒絕”頁面,具體效果體現在圖1中。

ASP.NET Core 6框架揭秘實例演示[40]:基于角色的授權

圖1 針對主頁的授權

[S2802]基于“策略”的授權

我們調用IAuthorizationService服務的AuthorizeAsync方法進行授權檢驗的時候,實際上是將授權要求定義在一個RolesAuthorizationRequirement對象中,這是一種比較煩瑣的編程方式。另一種推薦的做法是在應用啟動的過程中創建一系列通過AuthorizationPolicy對象表示的授權規則,并指定一個唯一的名稱對它們進行全局注冊,那么后續就可以針對注冊的策略名稱進行授權檢驗。如下面的代碼片段所示,在調用AddAuthorization擴展方法注冊授權相關服務時,我們利用作為輸入參數的Action<AuthorizationOptions>對象對授權策略進行了全局注冊。表示授權規策略的AuthorizationPolicy對象實際上是對基于角色“Admin”的RolesAuthorizationRequirement對象的封裝,我們調用AuthorizationOptions配置選項的AddPolicy方法對授權策略進行注冊,并將注冊名稱設置為“Home”。

using App;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using System.Security.Claims;
using System.Security.Principal;

var builder = WebApplication.CreateBuilder();
builder.Services
    .AddSingleton<IPageRenderer, PageRenderer>()
    .AddSingleton<IAccountService, AccountService>()
    .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
builder.Services.AddAuthorization(AddAuthorizationPolicy);
var app = builder.Build();
app.UseAuthentication();
app.Map("/", WelcomeAsync);
app.MapGet("Account/Login", Login);
app.MapPost("Account/Login", SignInAsync);
app.Map("Account/Logout", SignOutAsync);
app.Map("Account/AccessDenied", DenyAccess);
app.Run();

void AddAuthorizationPolicy(AuthorizationOptions options)
{
    var requirement = new RolesAuthorizationRequirement(new string[] { "admin" });
    var requirements = new IAuthorizationRequirement[] { requirement };
    var policy = new AuthorizationPolicy(requirements: requirements, authenticationSchemes: Array.Empty<string>());
    options.AddPolicy("Home", policy);
}
在呈現主頁的WelcomeAsync方法中,我們依然調用IAuthorizationService服務的AuthorizeAsync方法來檢驗用戶是否具有對應的權限,但這次采用的是另一個可以直接指定授權策略注冊名稱的AuthorizeAsync方法重載(S2802)。
async Task WelcomeAsync(HttpContext context, ClaimsPrincipal user, IPageRenderer renderer,
    IAuthorizationService authorizationService)
{
    if (user?.Identity?.IsAuthenticated ?? false)
    {
        var result = await authorizationService.AuthorizeAsync(user: user, policyName: "Home");
        if (result.Succeeded)
        {
            await renderer.RenderHomePage(user.Identity.Name!).ExecuteAsync(context);
        }
        else
        {
            await context.ForbidAsync();
        }
    }
    else
    {
      await  context.ChallengeAsync();
    }
}

[S2803]將“角色”綁定到路由終結點

上面演示的例子都調用IAuthorizationService對象的AuthorizeAsync方法來確定指定的用戶是否滿足提供的授權規則,實際上針對請求的授權直接交給AuthorizationMiddleware中間件來完成,該中間件可以采用如下的方式調用UseAuthorization擴展方法進行注冊。

...
var builder = WebApplication.CreateBuilder();
builder.Services
    .AddSingleton<IPageRenderer, PageRenderer>()
    .AddSingleton<IAccountService, AccountService>()
    .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
builder.Services.AddAuthorization();
var app = builder.Build();
app
    .UseAuthentication()
    .UseAuthorization();
...

當該中間件在進行授權檢驗的時候,會從當前終結點的元數據中提取授權規則,所以我們在注冊對應終結點的時候需要提供對應的授權規則。由于WelcomeAsync方法不再需要自行完成授權檢驗,所以它只需要將主頁呈現出來就可以了。針對“Admin”角色的授權要求直接利用標注在該方法上的AuthorizeAttribute特性來指定,該特性就是為AuthorizationMiddleware中間件提供授權規則的元數據(S2803)。

[Authorize(Roles ="admin")]
IResult WelcomeAsync(ClaimsPrincipal user, IPageRenderer renderer)=> renderer.RenderHomePage(user.Identity!.Name!);

[S2804]將“授權策略”綁定到路由終結點

如果在調用AddAuthorization擴展方法時已經定義了授權策略,我們也可以按照如下的方式將策略名稱設置為AuthorizeAttribute特性大的Policy屬性(S2804)。

[Authorize(Policy = "Home")]
IResult WelcomeAsync(ClaimsPrincipal user, IPageRenderer renderer) => renderer.RenderHomePage(user.Identity!.Name!);

如果采用Lambda表達式來定義終結點處理器,我們可以按照如下的方式將AuthorizeAttribute特性標注在表達式上。注冊終結點的各種Map方法會返回一個IEndpointConventionBuilder對象,我們可以安裝如下的方式調用它的RequireAuthorization擴展方法將AuthorizeAttribute特性作為一個IAuthorizeData對象添加到注冊終結點的元數據集合。RequireAuthorization擴展方法來有一個將授權策略名稱作為參數的重載。

app.Map("/",[Authorize(Roles ="admin")]ClaimsPrincipal user, IPageRenderer renderer)
    => renderer.RenderHomePage(user.Identity!.Name!));
app.Map("/",[Authorize(Policy = "Home")](ClaimsPrincipal user, IPageRenderer renderer)
    => renderer.RenderHomePage(user.Identity!.Name!));
app.Map("/", WelcomeAsync).RequireAuthorization(new AuthorizeAttribute {  Roles = "Admin"});
app.Map("/", WelcomeAsync).RequireAuthorization(new AuthorizeAttribute {  Policy = "Home"});
app.Map("/", WelcomeAsync).RequireAuthorization(policyNames: "Home");

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 99视频精品国在线视频艾草 | 国产综合亚洲欧美日韩一区二区 | 朝鲜美女免费一级毛片 | 91香蕉国产在线观看人员 | 亚洲高清视频免费 | 香蕉久久一区二区不卡无毒影院 | 国产成人综合亚洲一区 | 亚洲激情在线视频 | 久久偷拍免费2017 | 国产成人久视频免费 | 国产高清在线精品一区二区三区 | 好大好硬好紧太深了受不了 | 亚洲国产欧美另类va在线观看 | 亚洲第6页 | 久久九九精品国产自在现线拍 | 无码国产成人777爽死在线观看 | 亚洲男人的天堂在线 | 华人在线京东热 | 国产自产一区c | 午夜精品久久久久久久2023 | 好大好深好涨好烫还要 | 日韩免费在线视频 | 福利视频导航大全 | 国产精品久久香蕉免费播放 | 日本妇人成熟免费不卡片 | 操国产美女 | 国产99青草全福视在线 | 久久精品一区二区三区资源网 | 天天干天天操天天碰 | 日韩视频一 | gay男强壮军人chinese | 国产精品videosse | www.四色| yellow片在线观看 | 精品国产影院 | 秋霞理论一级在线观看手机版 | 国产综合亚洲专区在线 | 国产精品成人亚洲 | 国产成人精品高清在线观看99 | 国产精品模特hd在线 | 91色+91sesex|