实现权限访问控制列表

This commit is contained in:
administrator committed 2024-03-27 20:40:28 +08:00
1 parent 5a170f3024
commit 258781bdc5
17 files changed
+557 -7

No files matched your search

@@ -0,0 +1,10 @@
using System.Security.Claims;
namespace HelloShop.ServiceDefaults.Authorization;
public interface IPermissionChecker
{
Task<bool> IsGrantedAsync(string name, string? resourceType = null, string? resourceId = null);
Task<bool> IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name, string? resourceType = null, string? resourceId = null);
}
@@ -0,0 +1,49 @@
using HelloShop.ServiceDefaults.Constants;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using System.Security.Claims;
namespace HelloShop.ServiceDefaults.Authorization;
public abstract class PermissionChecker(IHttpContextAccessor httpContextAccessor, IDistributedCache distributedCache) : IPermissionChecker
{
protected HttpContext HttpContext { get; init; } = httpContextAccessor.HttpContext ?? throw new InvalidOperationException();
public async Task<bool> IsGrantedAsync(string name, string? resourceType = null, string? resourceId = null) => await IsGrantedAsync(HttpContext.User, name, resourceType, resourceId);
public async Task<bool> IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name, string? resourceType = null, string? resourceId = null)
{
var roleIds = claimsPrincipal.FindAll(CustomClaimTypes.RoleIdentifier).Select(c => Convert.ToInt32(c.Value)).ToArray();
foreach (var roleId in roleIds)
{
var cacheKey = PermissionGrantCacheItem.CreateCacheKey(roleId, name, resourceType, resourceId);
if (distributedCache.TryGetValue(cacheKey, out PermissionGrantCacheItem? cacheItem) && cacheItem != null)
{
if (cacheItem.IsGranted)
{
return true;
}
continue;
}
bool isGranted = await IsGrantedAsync(roleId, name, resourceType, resourceId);
await distributedCache.SetObjectAsync(cacheKey, new PermissionGrantCacheItem(isGranted), new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromMinutes(10)
});
if (isGranted)
{
return true;
}
}
return false;
}
public abstract Task<bool> IsGrantedAsync(int roleId, string name, string? resourceType = null, string? resourceId = null);
}
@@ -0,0 +1,23 @@
namespace HelloShop.ServiceDefaults.Authorization;
public class PermissionGrantCacheItem(bool isGranted = false)
{
public bool IsGranted { get; set; } = isGranted;
public static string CreateCacheKey(int roleId, string name, string? resourceType = null, string? resourceId = null)
{
string cacheKey = $"acl:role:{roleId}:{name}";
if (!string.IsNullOrWhiteSpace(resourceType))
{
cacheKey += $":{resourceType}";
}
if (!string.IsNullOrWhiteSpace(resourceId))
{
cacheKey += $":{resourceId}";
}
return cacheKey;
}
}
@@ -0,0 +1,12 @@
namespace HelloShop.ServiceDefaults.Authorization;
public class PermissionGrantedResponse
{
public required string Name { get; set; }
public string? ResourceType { get; set; }
public string? ResourceId { get; set; }
public bool IsGranted { get; set; }
}
@@ -0,0 +1,48 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using System.Net.Http.Headers;
using System.Net.Http.Json;
namespace HelloShop.ServiceDefaults.Authorization;
public class RemotePermissionChecker(IHttpContextAccessor httpContextAccessor, IDistributedCache distributedCache, IHttpClientFactory httpClientFactory, IOptions<RemotePermissionCheckerOptions> options) : PermissionChecker(httpContextAccessor, distributedCache)
{
private readonly RemotePermissionCheckerOptions _remotePermissionCheckerOptions = options.Value;
public override async Task<bool> IsGrantedAsync(int roleId, string name, string? resourceType = null, string? resourceId = null)
{
string? accessToken = await HttpContext.GetTokenAsync("access_token");
HttpClient httpClient = httpClientFactory.CreateClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
httpClient.BaseAddress = new Uri(_remotePermissionCheckerOptions.ApiEndpoint);
Dictionary<string, string?> parameters = new()
{
{ nameof(roleId), roleId.ToString() },
{ nameof(name), name },
{ nameof(resourceType) , resourceType },
{ nameof(resourceId), resourceId }
};
string queryString = QueryHelpers.AddQueryString(string.Empty, parameters);
var permissionGrants = httpClient.GetFromJsonAsAsyncEnumerable<PermissionGrantedResponse>(queryString);
await foreach (var permissionGrant in permissionGrants)
{
if (permissionGrant != null && permissionGrant.IsGranted)
{
return true;
}
}
return false;
}
}
@@ -0,0 +1,6 @@
namespace HelloShop.ServiceDefaults.Authorization;
public class RemotePermissionCheckerOptions
{
public string ApiEndpoint { get; set; } = default!;
}