设计权限定义和权限提供者
This commit is contained in:
parent
6742812bf2
commit
5a170f3024
@ -1,4 +1,4 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.9.34414.90
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
@ -18,8 +18,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloShop.ProductService",
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloShop.BasketService", "src\HelloShop.BasketService\HelloShop.BasketService.csproj", "{02EBA5AD-84B4-4AF4-B519-72061C08800D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloShop.HybridApp", "src\HelloShop.HybridApp\HelloShop.HybridApp.csproj", "{CC0E5839-B7E9-400E-9AF3-95863BBF518B}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{1AD03316-A743-4E9D-B3BC-FB9499D15141}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{29BE158E-825E-48AB-A02D-4E537A5DC502}"
|
||||
@ -66,12 +64,6 @@ Global
|
||||
{02EBA5AD-84B4-4AF4-B519-72061C08800D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{02EBA5AD-84B4-4AF4-B519-72061C08800D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{02EBA5AD-84B4-4AF4-B519-72061C08800D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CC0E5839-B7E9-400E-9AF3-95863BBF518B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CC0E5839-B7E9-400E-9AF3-95863BBF518B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CC0E5839-B7E9-400E-9AF3-95863BBF518B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{CC0E5839-B7E9-400E-9AF3-95863BBF518B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CC0E5839-B7E9-400E-9AF3-95863BBF518B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CC0E5839-B7E9-400E-9AF3-95863BBF518B}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{2022279A-E39F-4489-82AE-39AC53C594C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2022279A-E39F-4489-82AE-39AC53C594C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2022279A-E39F-4489-82AE-39AC53C594C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@ -93,7 +85,6 @@ Global
|
||||
{C4789097-3694-4370-9252-44268661A26E} = {1AD03316-A743-4E9D-B3BC-FB9499D15141}
|
||||
{B4C0ADA2-0442-4B7E-BFD9-AA39B52A0D42} = {1AD03316-A743-4E9D-B3BC-FB9499D15141}
|
||||
{02EBA5AD-84B4-4AF4-B519-72061C08800D} = {1AD03316-A743-4E9D-B3BC-FB9499D15141}
|
||||
{CC0E5839-B7E9-400E-9AF3-95863BBF518B} = {1AD03316-A743-4E9D-B3BC-FB9499D15141}
|
||||
{2022279A-E39F-4489-82AE-39AC53C594C9} = {29BE158E-825E-48AB-A02D-4E537A5DC502}
|
||||
{45932B7F-6ED0-40F3-AA2C-F14A844FEE18} = {29BE158E-825E-48AB-A02D-4E537A5DC502}
|
||||
EndGlobalSection
|
||||
|
@ -12,19 +12,20 @@ namespace HelloShop.IdentityService.Controllers
|
||||
public class TestsController : ControllerBase
|
||||
{
|
||||
[HttpGet(nameof(Foo))]
|
||||
[Authorize(Roles = "AdminRole")]
|
||||
[Authorize(IdentityPermissions.Users.Update)]
|
||||
public IActionResult Foo()
|
||||
{
|
||||
return Ok("Hello, World!");
|
||||
}
|
||||
|
||||
[HttpGet(nameof(Bar))]
|
||||
[Authorize(Roles = "GuestRole")]
|
||||
[Authorize(IdentityPermissions.Users.Create)]
|
||||
public IActionResult Bar()
|
||||
{
|
||||
return Ok("Hello, World!");
|
||||
}
|
||||
|
||||
[Authorize(IdentityPermissions.Users.Delete)]
|
||||
[HttpGet(nameof(Baz))]
|
||||
public IActionResult Baz()
|
||||
{
|
||||
|
@ -0,0 +1,25 @@
|
||||
using HelloShop.ServiceDefaults.Permissions;
|
||||
|
||||
namespace HelloShop.IdentityService;
|
||||
|
||||
public class IdentityPermissionDefinitionProvider : IPermissionDefinitionProvider
|
||||
{
|
||||
public void Define(PermissionDefinitionContext context)
|
||||
{
|
||||
var identityGroup = context.AddGroup(IdentityPermissions.GroupName, "访问控制");
|
||||
|
||||
var roles = identityGroup.AddPermission(IdentityPermissions.Roles.Default, "角色管理");
|
||||
|
||||
roles.AddChild(IdentityPermissions.Roles.Create, "创建角色");
|
||||
roles.AddChild(IdentityPermissions.Roles.Update, "更新角色");
|
||||
roles.AddChild(IdentityPermissions.Roles.Delete, "删除角色");
|
||||
roles.AddChild(IdentityPermissions.Roles.ManagePermissions, "管理角色权限");
|
||||
|
||||
var users = identityGroup.AddPermission(IdentityPermissions.Users.Default, "用户管理");
|
||||
|
||||
users.AddChild(IdentityPermissions.Users.Create, "创建用户");
|
||||
users.AddChild(IdentityPermissions.Users.Update, "更新用户");
|
||||
users.AddChild(IdentityPermissions.Users.Delete, "删除用户");
|
||||
users.AddChild(IdentityPermissions.Users.ManageRoles, "管理用户角色");
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
namespace HelloShop.IdentityService;
|
||||
|
||||
public static class IdentityPermissions
|
||||
{
|
||||
public const string GroupName = "Identity";
|
||||
|
||||
public static class Roles
|
||||
{
|
||||
public const string Default = GroupName + ".Roles";
|
||||
public const string Create = Default + ".Create";
|
||||
public const string Update = Default + ".Update";
|
||||
public const string Delete = Default + ".Delete";
|
||||
public const string ManagePermissions = Default + ".ManagePermissions";
|
||||
}
|
||||
|
||||
public static class Users
|
||||
{
|
||||
public const string Default = GroupName + ".Users";
|
||||
public const string Create = Default + ".Create";
|
||||
public const string Update = Default + ".Update";
|
||||
public const string Delete = Default + ".Delete";
|
||||
public const string ManageRoles = Update + ".ManageRoles";
|
||||
}
|
||||
}
|
@ -53,6 +53,7 @@ builder.Services.AddAuthentication(options =>
|
||||
|
||||
builder.Services.AddDataSeedingProviders();
|
||||
builder.Services.AddOpenApi();
|
||||
builder.Services.AddPermissionDefinitions();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
@ -66,5 +67,6 @@ app.MapControllers();
|
||||
|
||||
app.UseDataSeedingProviders();
|
||||
app.UseOpenApi();
|
||||
app.MapGroup("api/Permissions").MapPermissionDefinitions("Permissions");
|
||||
|
||||
app.Run();
|
||||
|
@ -0,0 +1,65 @@
|
||||
using HelloShop.ServiceDefaults.Permissions;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Reflection;
|
||||
|
||||
namespace HelloShop.ServiceDefaults.Extensions;
|
||||
|
||||
public static class PermissionExtensions
|
||||
{
|
||||
public static IServiceCollection AddPermissionDefinitions(this IServiceCollection services, Assembly? assembly = null)
|
||||
{
|
||||
assembly ??= Assembly.GetCallingAssembly();
|
||||
|
||||
var permissionDefinitionProviders = assembly.ExportedTypes.Where(t => t.IsAssignableTo(typeof(IPermissionDefinitionProvider)));
|
||||
|
||||
permissionDefinitionProviders.ToList().ForEach(t => services.AddSingleton(typeof(IPermissionDefinitionProvider), t));
|
||||
|
||||
services.AddSingleton<IPermissionDefinitionManager, PermissionDefinitionManager>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IEndpointRouteBuilder MapPermissionDefinitions(this IEndpointRouteBuilder endpoints, params string[] tags)
|
||||
{
|
||||
var routeGroup = endpoints.MapGroup(string.Empty);
|
||||
|
||||
routeGroup.MapGet("PermissionDefinitions", async (IPermissionDefinitionManager permissionDefinitionManager) =>
|
||||
{
|
||||
List<PermissionGroupDefinitionResponse> result = [];
|
||||
|
||||
var permissionGroups = await permissionDefinitionManager.GetGroupsAsync();
|
||||
|
||||
foreach (var permissionGroup in permissionGroups)
|
||||
{
|
||||
PermissionGroupDefinitionResponse permissionGroupDefinition = new()
|
||||
{
|
||||
Name = permissionGroup.Name,
|
||||
DisplayName = permissionGroup.DisplayName,
|
||||
Permissions = []
|
||||
};
|
||||
|
||||
foreach (PermissionDefinition? permission in permissionGroup.GetPermissionsWithChildren())
|
||||
{
|
||||
PermissionDefinitionResponse permissionDefinition = new()
|
||||
{
|
||||
Name = permission.Name,
|
||||
DisplayName = permission.DisplayName,
|
||||
ParentName = permission.Parent?.Name
|
||||
};
|
||||
|
||||
permissionGroupDefinition.Permissions.Add(permissionDefinition);
|
||||
}
|
||||
|
||||
result.Add(permissionGroupDefinition);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}).WithTags(tags);
|
||||
|
||||
return routeGroup;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
namespace HelloShop.ServiceDefaults.Permissions;
|
||||
|
||||
public class PermissionDefinitionResponse
|
||||
{
|
||||
public required string Name { get; init; }
|
||||
|
||||
public string? DisplayName { get; set; }
|
||||
|
||||
public string? ParentName { get; set; }
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
namespace HelloShop.ServiceDefaults.Permissions;
|
||||
|
||||
public class PermissionGroupDefinitionResponse
|
||||
{
|
||||
public required string Name { get; init; }
|
||||
|
||||
public string? DisplayName { get; set; }
|
||||
|
||||
public required List<PermissionDefinitionResponse> Permissions { get; init; }
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace HelloShop.ServiceDefaults.Permissions;
|
||||
|
||||
public interface IPermissionDefinitionManager
|
||||
{
|
||||
Task<PermissionDefinition> GetAsync(string name);
|
||||
|
||||
Task<PermissionDefinition?> GetOrNullAsync(string name);
|
||||
|
||||
Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync();
|
||||
|
||||
Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync();
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
namespace HelloShop.ServiceDefaults.Permissions;
|
||||
|
||||
public interface IPermissionDefinitionProvider
|
||||
{
|
||||
void Define(PermissionDefinitionContext context);
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
namespace HelloShop.ServiceDefaults.Permissions;
|
||||
|
||||
public class PermissionDefinition
|
||||
{
|
||||
public string Name { get; } = default!;
|
||||
|
||||
public string? DisplayName { get; set; }
|
||||
|
||||
public PermissionDefinition? Parent { get; private set; }
|
||||
|
||||
public bool IsEnabled { get; set; }
|
||||
|
||||
private readonly List<PermissionDefinition> _children = [];
|
||||
|
||||
public IReadOnlyList<PermissionDefinition> Children => [.. _children];
|
||||
|
||||
protected internal PermissionDefinition(string name, string? displayName = null, bool isEnabled = true)
|
||||
{
|
||||
Name = name;
|
||||
DisplayName = displayName;
|
||||
IsEnabled = isEnabled;
|
||||
}
|
||||
|
||||
public virtual PermissionDefinition AddChild(string name, string? displayName = null, bool isEnabled = true)
|
||||
{
|
||||
var child = new PermissionDefinition(name, displayName, isEnabled) { Parent = this };
|
||||
_children.Add(child);
|
||||
return child;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
namespace HelloShop.ServiceDefaults.Permissions;
|
||||
|
||||
public class PermissionDefinitionContext
|
||||
{
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
|
||||
internal Dictionary<string, PermissionGroupDefinition> Groups { get; }
|
||||
|
||||
internal PermissionDefinitionContext(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
Groups = [];
|
||||
}
|
||||
|
||||
public virtual PermissionGroupDefinition AddGroup(string name, string? displayName = null)
|
||||
{
|
||||
if (Groups.ContainsKey(name))
|
||||
{
|
||||
throw new InvalidOperationException($"There is already an existing permission group with name: {name}");
|
||||
}
|
||||
|
||||
return Groups[name] = new PermissionGroupDefinition(name, displayName);
|
||||
}
|
||||
|
||||
public virtual PermissionGroupDefinition GetGroup(string name)
|
||||
{
|
||||
PermissionGroupDefinition? group = GetGroupOrNull(name);
|
||||
|
||||
return group is null ? throw new InvalidOperationException($"Could not find a permission definition group with the given name: {name}") : group;
|
||||
}
|
||||
|
||||
public virtual PermissionGroupDefinition? GetGroupOrNull(string name)
|
||||
{
|
||||
if (!Groups.TryGetValue(name, out PermissionGroupDefinition? value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public virtual void RemoveGroup(string name)
|
||||
{
|
||||
if (!Groups.ContainsKey(name))
|
||||
{
|
||||
throw new InvalidOperationException($"Not found permission group with name: {name}");
|
||||
}
|
||||
|
||||
Groups.Remove(name);
|
||||
}
|
||||
|
||||
public virtual PermissionDefinition? GetPermissionOrNull(string name)
|
||||
{
|
||||
foreach (var groupDefinition in Groups.Values)
|
||||
{
|
||||
var permissionDefinition = groupDefinition.GetPermissionOrNull(name);
|
||||
|
||||
if (permissionDefinition != null)
|
||||
{
|
||||
return permissionDefinition;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace HelloShop.ServiceDefaults.Permissions;
|
||||
|
||||
public class PermissionDefinitionManager : IPermissionDefinitionManager
|
||||
{
|
||||
private readonly Lazy<Dictionary<string, PermissionGroupDefinition>> _lazyPermissionGroupDefinitions;
|
||||
|
||||
protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value;
|
||||
|
||||
private readonly Lazy<Dictionary<string, PermissionDefinition>> _lazyPermissionDefinitions;
|
||||
|
||||
protected IDictionary<string, PermissionDefinition> PermissionDefinitions => _lazyPermissionDefinitions.Value;
|
||||
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public PermissionDefinitionManager(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_lazyPermissionGroupDefinitions = new Lazy<Dictionary<string, PermissionGroupDefinition>>(CreatePermissionGroupDefinitions, isThreadSafe: true);
|
||||
_lazyPermissionDefinitions = new Lazy<Dictionary<string, PermissionDefinition>>(CreatePermissionDefinitions, isThreadSafe: true);
|
||||
}
|
||||
|
||||
protected virtual Dictionary<string, PermissionGroupDefinition> CreatePermissionGroupDefinitions()
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
|
||||
var context = new PermissionDefinitionContext(scope.ServiceProvider);
|
||||
|
||||
var providers = _serviceProvider.GetServices<IPermissionDefinitionProvider>();
|
||||
|
||||
foreach (IPermissionDefinitionProvider provider in providers)
|
||||
{
|
||||
provider.Define(context);
|
||||
}
|
||||
|
||||
return context.Groups;
|
||||
}
|
||||
|
||||
protected virtual Dictionary<string, PermissionDefinition> CreatePermissionDefinitions()
|
||||
{
|
||||
var permissions = new Dictionary<string, PermissionDefinition>();
|
||||
|
||||
foreach (var groupDefinition in PermissionGroupDefinitions.Values)
|
||||
{
|
||||
foreach (var permission in groupDefinition.Permissions)
|
||||
{
|
||||
AddPermissionToDictionaryRecursively(permissions, permission);
|
||||
}
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
protected virtual void AddPermissionToDictionaryRecursively(Dictionary<string, PermissionDefinition> permissions, PermissionDefinition permission)
|
||||
{
|
||||
if (permissions.ContainsKey(permission.Name))
|
||||
{
|
||||
throw new InvalidOperationException($"Duplicate permission name {permission.Name}");
|
||||
}
|
||||
|
||||
permissions[permission.Name] = permission;
|
||||
|
||||
foreach (var child in permission.Children)
|
||||
{
|
||||
AddPermissionToDictionaryRecursively(permissions, child);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PermissionDefinition> GetAsync(string name)
|
||||
{
|
||||
var permission = await GetOrNullAsync(name);
|
||||
|
||||
return permission ?? throw new InvalidOperationException($"Undefined permission {name}");
|
||||
}
|
||||
|
||||
public Task<PermissionDefinition?> GetOrNullAsync(string name)
|
||||
{
|
||||
return Task.FromResult(PermissionDefinitions.TryGetValue(name, out var obj) ? obj : default);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
|
||||
{
|
||||
IReadOnlyList<PermissionGroupDefinition> permissionGroups = [.. PermissionGroupDefinitions.Values];
|
||||
|
||||
return Task.FromResult(permissionGroups);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
|
||||
{
|
||||
IReadOnlyList<PermissionDefinition> permissions = [.. PermissionDefinitions.Values];
|
||||
|
||||
return Task.FromResult(permissions);
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
namespace HelloShop.ServiceDefaults.Permissions;
|
||||
|
||||
public class PermissionGroupDefinition
|
||||
{
|
||||
public string Name { get; } = default!;
|
||||
|
||||
public string? DisplayName { get; set; }
|
||||
|
||||
private readonly List<PermissionDefinition> _permissions = [];
|
||||
|
||||
public IReadOnlyList<PermissionDefinition> Permissions => [.. _permissions];
|
||||
|
||||
protected internal PermissionGroupDefinition(string name, string? displayName = null)
|
||||
{
|
||||
Name = name;
|
||||
DisplayName = displayName;
|
||||
}
|
||||
|
||||
public virtual PermissionDefinition AddPermission(string name, string? displayName = null, bool isEnabled = true)
|
||||
{
|
||||
var permission = new PermissionDefinition(name, displayName, isEnabled);
|
||||
_permissions.Add(permission);
|
||||
return permission;
|
||||
}
|
||||
|
||||
public virtual List<PermissionDefinition> GetPermissionsWithChildren()
|
||||
{
|
||||
var permissions = new List<PermissionDefinition>();
|
||||
|
||||
foreach (var permission in _permissions)
|
||||
{
|
||||
AddPermissionToListRecursively(permissions, permission);
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
private static void AddPermissionToListRecursively(List<PermissionDefinition> permissions, PermissionDefinition permission)
|
||||
{
|
||||
permissions.Add(permission);
|
||||
|
||||
foreach (var child in permission.Children)
|
||||
{
|
||||
AddPermissionToListRecursively(permissions, child);
|
||||
}
|
||||
}
|
||||
|
||||
public PermissionDefinition? GetPermissionOrNull(string name) => GetPermissionOrNullRecursively(Permissions, name);
|
||||
|
||||
private static PermissionDefinition? GetPermissionOrNullRecursively(IReadOnlyList<PermissionDefinition> permissions, string name)
|
||||
{
|
||||
foreach (var permission in permissions)
|
||||
{
|
||||
if (permission.Name == name)
|
||||
{
|
||||
return permission;
|
||||
}
|
||||
|
||||
var childPermission = GetPermissionOrNullRecursively(permission.Children, name);
|
||||
if (childPermission != null)
|
||||
{
|
||||
return childPermission;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user