上篇讲到Blazor WebAssembly前端实现了简单的登录授权验证,但是无法结合我们系统后端配置的权限做相应的策略授权。本篇就讲一下如何自定义实现基于策略的授权。
要用两个步骤来实现
1、后端把权限信息读取到前端缓存。
后端实现一个接口,以下是代码:
/// <summary>
/// 是否有权限
/// </summary>
/// <param name="policy"></param>
/// <returns></returns>
public async Task<List<PermissionGrantedDto>> AuthorizeAsync(List<string> policys)
{
List<PermissionGrantedDto> list = new List<PermissionGrantedDto>();
foreach (string policy in policys)
{
bool isGranted= (await AuthorizationService.AuthorizeAsync(policy)).Succeeded;
PermissionGrantedDto permissionGrantedDto = new PermissionGrantedDto();
permissionGrantedDto.PermissionName = policy;
permissionGrantedDto.IsGranted = isGranted;
list.Add(permissionGrantedDto);
}
return list;
}
通过这个接口能实现前端把所有权限名称传到后端来获取到是否有授权信息返回给前端。
前端就要实现以下几个地方:
1、存储权限名称的
public class PermissionData
{
public const string CategoryManagement = "Category_Management";
public const string CategoryManagementCreate = "Category_Management_Create";
public const string CategoryManagementEdit = "Category_Management_Edit";
public const string CategoryManagementDelete = "Category_Management_Delete";
}
2、缓存权限授权信息的
public class PermissionGrantedCache
{
/// <summary>
/// 权限名
/// </summary>
public string PermissionName { get; set; }
/// <summary>
/// 是否有权限
/// </summary>
public bool IsGranted { get; set; }
}
以上就能实现了前端把权限名称传到后端获取授权信息,然后缓存到前端。
接下来就是实现如何自定义实现权限是授权判断了
首先继承DefaultAuthorizationPolicyProvider实现
public class CustomAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
{
private readonly AuthorizationOptions _options;
public CustomAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options): base(options)
{
_options = options.Value;
}
public override async Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
var policy = await base.GetPolicyAsync(policyName);
if (policy != null)
{
return policy;
}
var policyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
policyBuilder.Requirements.Add(new PermissionRequirement(policyName));
return policyBuilder.Build();
}
}
public class PermissionRequirement : IAuthorizationRequirement
{
public string PermissionName { get; }
public PermissionRequirement(string permissionName)
{
PermissionName = permissionName;
}
}
然后继承AuthorizationHandler实现
public class PermissionRequirementHandler : AuthorizationHandler<PermissionRequirement>
{
private static List<PermissionGrantedCache> _permissionGrantedCaches;
private readonly TokenHttpClient _tokenHttpClient;
public PermissionRequirementHandler(TokenHttpClient tokenHttpClient)
{
_tokenHttpClient = tokenHttpClient;
}
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
PermissionRequirement requirement)
{
if ((await PermissionGranted(requirement.PermissionName)))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
//return Task.CompletedTask;
}
protected async Task<bool> PermissionGranted(string permissionName)
{
_permissionGrantedCaches= _permissionGrantedCaches ?? new List<PermissionGrantedCache>();
var grantedCache = _permissionGrantedCaches.FirstOrDefault(p => p.PermissionName == permissionName);
if (grantedCache != null)
{
return grantedCache.IsGranted;
}
else
{
_permissionGrantedCaches.Clear();
//获取所有权限
FieldInfo[] fis = typeof(PermissionData).GetFields();
List<string> pinfoList = new List<string>();
foreach (FieldInfo pinfo in fis)
{
pinfoList.Add(pinfo.GetRawConstantValue().ToString());
}
var response = await _tokenHttpClient.PostAsJsonWithTokenAsync($"/api/Test/authorization/authorize", pinfoList);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
_permissionGrantedCaches.AddRange(JsonConvert.DeserializeObject<List<PermissionGrantedCache>>(content));
var grantedCacheNew = _permissionGrantedCaches.FirstOrDefault(p => p.PermissionName == permissionName);
if (grantedCacheNew != null)
{
return grantedCacheNew.IsGranted;
}
else
{
return false;
}
}
else
{
return false;
}
}
}
}
最后在program.cs里注入
builder.Services.AddScoped<CustomAuthorizationPolicyProvider>();
builder.Services.AddScoped<IAuthorizationPolicyProvider>(s => s.GetRequiredService<CustomAuthorizationPolicyProvider>());
builder.Services.AddScoped<IAuthorizationHandler, PermissionRequirementHandler>();
builder.Services.AddSingleton<PermissionData, PermissionData>();
builder.Services.AddSingleton<PermissionGrantedCache, PermissionGrantedCache>();
怎么使用呢?看使用办法:
@page "/commodities/categorymanage"
@attribute [Authorize(PermissionData.CategoryManagement)]
<h1>类别设置</h1>
@code {
}
这样就实现了自定义的权限授权了。