現在BootstrapBlazor
處於大更新時期,Menu
元件要改為泛型模式。
本來我們的這一篇應該是把Layout
改了,但是改Layout
肯定要涉及到選單,如果現在寫了呢,就進入一個釋出就過時的狀態,就很尷尬,所以後面的就稍微拖一拖。
加上昨天有人說我用OnNavigateAsync
違反單一性原則,要用策略,所以這裡我們說下策略怎麼做。
首先我們要有一個實現IAuthorizationRequirement
介面的類,這個類沒有什麼特別的要求,我們就寫一個空類來處理。
public class AdminRequirement : IAuthorizationRequirement
{
}
然後要寫一個Handler,來繼承這個AuthorizationHandler<AdminRequirement>
,其中泛型是我們上面的實現介面的類。
public class AdminRequirementHandler : AuthorizationHandler<AdminRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AdminRequirement requirement)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
}
實現HandleRequirementAsync
方法,這個方法就是我們的關鍵方法,授權的實現就在這裡面。
其中預設的授權狀態是Fail
,如果我們希望允許通過,就執行context.Succeed(requirement);
來告訴策略我們認證成功了。
在Program.cs
裡我們需要把這兩個都註冊進去,首先註冊我們的Handler
builder.Services.AddSingleton<IAuthorizationHandler, AdminRequirementHandler>();
然後註冊我們的授權策略
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy => policy.Requirements.Add(new AdminRequirement()));
});
這裡的Admin
就是我們的策略名字。
在我們需要認證的位置增加特性@attribute [Authorize(Policy = "Admin")]
,然後在我們的授權策略裡打斷點,應該就會發現斷點進入了。
因為Blazor裡面我們拿不到HttpContext
,所以沒法用Request.Path
的方式來拿到url,所以只能使用將RouteData
作為Resource
傳入,然後使用attribute
的方式拿到。
這裡我們在App.razor
裡傳入routeData
<AuthorizeRouteView Resource="@routeData" RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<RedirectToLogin></RedirectToLogin>
</NotAuthorized>
</AuthorizeRouteView>
然後修改HandleRequirementAsync
。
if (context.User.Identity?.IsAuthenticated != true)
{
return Task.CompletedTask;
}
if (!int.TryParse(context.User.FindFirst(ClaimTypes.Role)?.Value, out var roleId))
{
return Task.CompletedTask;
}
if (context.Resource is RouteData routeData)
{
var routeAttr = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
x.AttributeType == typeof(RouteAttribute));
if (routeAttr == null)
{
context.Succeed(requirement);
}
else
{
var url = routeAttr.ConstructorArguments[0].Value as string;
var permission = PermissionEntity
.Where(x => x.Roles!.Any(y => y.Id == roleId) && x.Url == url).First();
if (permission != null)
{
context.Succeed(requirement);
}
}
}
return Task.CompletedTask;
}
這裡跟上一篇的處理思路整體一樣,首先我們判斷如果使用者都沒登入,那就直接失敗,如果登入了我們就去拿RoleId
,拿不到自然就失敗。
不同點在下面,我們沒法直接拿到Path
,所以我們只能去找RouteAttribute
,其實就是我們的@page
路由。這裡我們也可以自己定義一個Attribute
取自己的。
如果我們沒找到這個,證明這應該不是個blazor頁面,我們就暫時讓它成功。
如果找到了,那麼我們就找routeAttr.ConstructorArguments[0].Value as string
,這裡面就是對應的路由地址了。
下面就跟之前一樣,用路由地址來判斷是否是又許可權就行了。
原始碼在github: https://github.com/j4587698/BlazorLearn,分支lesson6