chore: 同步当前开发内容
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using TakeoutSaaS.Module.Authorization.Policies;
|
||||
|
||||
namespace TakeoutSaaS.Module.Authorization.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// 权限校验特性
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public sealed class PermissionAuthorizeAttribute : AuthorizeAttribute
|
||||
{
|
||||
public PermissionAuthorizeAttribute(params string[] permissions)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(permissions);
|
||||
var normalized = permissions
|
||||
.Where(p => !string.IsNullOrWhiteSpace(p))
|
||||
.Select(p => p.Trim())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
|
||||
if (normalized.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("至少需要一个权限标识", nameof(permissions));
|
||||
}
|
||||
|
||||
Permissions = normalized;
|
||||
Policy = PermissionAuthorizationPolicyProvider.BuildPolicyName(normalized);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 所需权限集合
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string> Permissions { get; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using TakeoutSaaS.Module.Authorization.Policies;
|
||||
|
||||
namespace TakeoutSaaS.Module.Authorization.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// 权限授权注入扩展
|
||||
/// </summary>
|
||||
public static class AuthorizationServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 启用自定义权限策略提供者与处理器
|
||||
/// </summary>
|
||||
public static IServiceCollection AddPermissionAuthorization(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IAuthorizationPolicyProvider, PermissionAuthorizationPolicyProvider>();
|
||||
services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace TakeoutSaaS.Module.Authorization.Policies;
|
||||
|
||||
/// <summary>
|
||||
/// 权限校验处理器
|
||||
/// </summary>
|
||||
public sealed class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
|
||||
{
|
||||
public const string PermissionClaimType = "permission";
|
||||
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
|
||||
{
|
||||
if (context.User?.Identity?.IsAuthenticated != true)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var userPermissions = context.User
|
||||
.FindAll(PermissionClaimType)
|
||||
.Select(claim => claim.Value)
|
||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||
.Select(value => value.Trim())
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (userPermissions.Count == 0)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (requirement.Permissions.Any(userPermissions.Contains))
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace TakeoutSaaS.Module.Authorization.Policies;
|
||||
|
||||
/// <summary>
|
||||
/// 权限策略提供者(按需动态构建策略)
|
||||
/// </summary>
|
||||
public sealed class PermissionAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
|
||||
{
|
||||
public const string PolicyPrefix = "PERMISSION:";
|
||||
private readonly AuthorizationOptions _options;
|
||||
|
||||
public PermissionAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options)
|
||||
: base(options)
|
||||
{
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
public override Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
|
||||
{
|
||||
if (policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var existingPolicy = _options.GetPolicy(policyName);
|
||||
if (existingPolicy != null)
|
||||
{
|
||||
return Task.FromResult(existingPolicy);
|
||||
}
|
||||
|
||||
var permissions = ParsePermissions(policyName);
|
||||
if (permissions.Length == 0)
|
||||
{
|
||||
return Task.FromResult<AuthorizationPolicy?>(null);
|
||||
}
|
||||
|
||||
var policy = new AuthorizationPolicyBuilder()
|
||||
.AddRequirements(new PermissionRequirement(permissions))
|
||||
.Build();
|
||||
|
||||
_options.AddPolicy(policyName, policy);
|
||||
return Task.FromResult<AuthorizationPolicy?>(policy);
|
||||
}
|
||||
|
||||
return base.GetPolicyAsync(policyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据权限集合构建策略名称
|
||||
/// </summary>
|
||||
public static string BuildPolicyName(IEnumerable<string> permissions)
|
||||
=> $"{PolicyPrefix}{string.Join('|', NormalizePermissions(permissions))}";
|
||||
|
||||
private static string[] ParsePermissions(string policyName)
|
||||
{
|
||||
var raw = policyName[PolicyPrefix.Length..];
|
||||
return NormalizePermissions(raw.Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
|
||||
}
|
||||
|
||||
private static string[] NormalizePermissions(IEnumerable<string> permissions)
|
||||
=> permissions
|
||||
.Where(p => !string.IsNullOrWhiteSpace(p))
|
||||
.Select(p => p.Trim())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace TakeoutSaaS.Module.Authorization.Policies;
|
||||
|
||||
/// <summary>
|
||||
/// 权限要求
|
||||
/// </summary>
|
||||
public sealed class PermissionRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public PermissionRequirement(IReadOnlyCollection<string> permissions)
|
||||
{
|
||||
Permissions = permissions ?? throw new ArgumentNullException(nameof(permissions));
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<string> Permissions { get; }
|
||||
}
|
||||
@@ -4,8 +4,11 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\TakeoutSaaS.Shared.Abstractions\TakeoutSaaS.Shared.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
namespace TakeoutSaaS.Module.Identity.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// 微信登录服务抽象(code2Session)
|
||||
/// </summary>
|
||||
public interface IWeChatAuthService
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用小程序登录 code 换取 openid/unionid/session_key
|
||||
/// </summary>
|
||||
Task<WeChatSessionInfo> Code2SessionAsync(string code, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 微信会话信息
|
||||
/// </summary>
|
||||
public sealed class WeChatSessionInfo
|
||||
{
|
||||
public string OpenId { get; init; } = string.Empty;
|
||||
public string? UnionId { get; init; }
|
||||
public string SessionKey { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\TakeoutSaaS.Shared.Abstractions\TakeoutSaaS.Shared.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user