从声明中检查 ABP 权限

Checking ABP permissions from claims

我正在为 ASP.NET Core 使用 ABP 3.9 版。我们有一个现有的 Identity Server 4 实例,它以声明的形式(通过 OIDC)提供角色信息。我想连接到 ABP 的权限系统以进行动态菜单操作等。由于我没有使用本地 Identity Server1 实现,因此我看不到将声明转换为权限的方法。

我的想法是使用自定义中间件这样处理:

public class ClaimsToAbpPermissionsMiddleware
{
    private readonly RequestDelegate _next;

    public ClaimsToAbpPermissionsMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Get user
        ClaimsPrincipal user = context.User;

        // foreach claim of type "role"
        var roleClaims = user.Claims.Where(claim => claim.Type == "role");
        foreach (Claim claim in roleClaims)
        {
            switch (claim.Value)
            {
                case "TestResults":
                    // Assign applicable permission
                    // ...
                    break;
                default:
                    break;
            }
        }

        // Call the next delegate/middleware in the pipeline
        await _next(context);
    }
}

问题是我不知道如何申请权限。 User Management2 建议使用 UserManager,它似乎具有必要的功能。

首先,Abp.ZeroCore没有这个class。我可以在 ASP.NET 核心应用程序中使用 Abp.Zero 吗?

其次,看起来 Abp.Authorization.Users.AbpUserManager 具有类似的功能。我可以用这个吗?但是,我不确定如何将它注入 Startup,因为它需要一些类型来注入 AbpUserManager<TRole, TUser>,而且我不清楚要使用什么类型。

如有任何帮助,我们将不胜感激。 另外,我正在尝试可行吗?有没有更好的方法?

问题是我没有使用 Module Zero 的 User Management2。我们所有的用户都在一个单独的 ASP.NET 身份(身份服务器)实现中,我使用的是来自 ABP 的模板,它不包含 user/tenant/role 的 models/pages,所以我不没有要注入的类型 AbpUserManager<TRole, TUser>.

1https://aspnetboilerplate.com/Pages/Documents/Zero/Identity-Server
2https://aspnetboilerplate.com/Pages/Documents/Zero/User-Management

更新: 所以我根据以下答案实施了 IPermissionChecker:

PermissionChecker(不喜欢这个开关,但一旦它工作了我就会重构):

public class PermissionChecker : IPermissionChecker, ITransientDependency
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public PermissionChecker(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public  Task<bool> IsGrantedAsync(string permissionName)
    {
        bool isGranted = false;

        // Get user
        var user = _httpContextAccessor.HttpContext.User;

        // Get claims of type "role"
        var roleClaims = user.Claims.Where(claim => claim.Type == "role");

        // Check for applicable permission based on role permissions
        foreach (Claim claim in roleClaims)
        {
            switch (claim.Value)
            {
                case "TestResults":
                    // Assign applicable permission
                    // ...
                    if(permissionName.ToLowerInvariant() == "TestResults".ToLowerInvariant())
                    {
                        isGranted = true;
                    }
                    break;
                case "About":
                    // Assign applicable permission
                    // ...
                    if (permissionName.ToLowerInvariant() == "AboutView".ToLowerInvariant())
                    {
                        isGranted = true;
                    }
                    break;
                case "Account":
                    // Assign applicable permission
                    // ...
                    if (permissionName.ToLowerInvariant() == "AccountView".ToLowerInvariant())
                    {
                        isGranted = true;
                    }
                    break;
                default:
                    break;
            }
            if (isGranted)
            {
                break;
            }
        }
        //return new Task<bool> 
        return Task.FromResult(isGranted);
    }

    public Task<bool> IsGrantedAsync(UserIdentifier user, string permissionName)
    {
        return IsGrantedAsync(permissionName);
    }
}

授权提供者:

public class MyAuthProvider : AuthorizationProvider
{
    public override void SetPermissions(IPermissionDefinitionContext context)
    {
        var about = context.CreatePermission("AboutView");
        var account = context.CreatePermission("AccountView");
        var testResults = context.CreatePermission("TestResults");
    }
}

模块初始化:

public class CentralPortalCoreModule : AbpModule
{
    public override void PreInitialize()
    {
        Configuration.Auditing.IsEnabledForAnonymousUsers = true;

        CentralPortalLocalizationConfigurer.Configure(Configuration.Localization);
        IocManager.Register<IPermissionChecker, PermissionChecker>(DependencyLifeStyle.Transient);
        Configuration.Authorization.Providers.Add<MyAuthProvider>();
    }

    public override void Initialize()
    {
        IocManager.RegisterAssemblyByConvention(typeof(CentralPortalCoreModule).GetAssembly());
    }
}

导航提供商:

public override void SetNavigation(INavigationProviderContext context)
    {
        context.Manager.MainMenu
            .AddItem(
                new MenuItemDefinition(
                    PageNames.Home,
                    L("HomePage"),
                    url: "",
                    icon: "fa fa-home"
                    )
            ).AddItem(
                new MenuItemDefinition(
                    PageNames.About,
                    L("About"),
                    url: "Home/About",
                    icon: "fa fa-info",
                    requiredPermissionName: "AboutView",
                    requiresAuthentication: true
                    )
            ).AddItem(
                new MenuItemDefinition(
                "Results",
                L("Results"),
                url: "Results",
                icon: "fa fa-tasks",
                requiredPermissionName: "TestResults"
                )
            )
            .AddItem(
                new MenuItemDefinition(
                    PageNames.Account,
                    L("Account"),
                    url: "Account",
                    icon: "fa fa-info",
                    requiredPermissionName:"AccountView",
                    requiresAuthentication: true

                    )

            )
            .AddItem(
                new MenuItemDefinition(
                    PageNames.Contact,
                    L("Contact"),
                    url: "Contact",
                    icon: "fa fa-info"
                    )
            );
    }

添加断点表明 PermissionChecker 与 AuthProvider 一样被初始化。不幸的是,即使我已通过身份验证并具有有效的角色声明,导航也不会显示受保护的项目。永远不会调用 IsGrantedAsync 方法。我尝试使用 requiresAuthentication = true 和 false 设置导航项目,但都没有改变任何东西。

我是不是漏掉了什么?

好吧,您指的是模块零文档,而不是使用模块零。

如果您不存储用户,那么存储用户权限可能没有意义。

您可以实施 IPermissionChecker 来检查声明中的权限。

public class PermissionChecker : IPermissionChecker, ITransientDependency
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public PermissionChecker(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<bool> IsGrantedAsync(string permissionName)
    {
        // Get user
        var user = _httpContextAccessor.HttpContext.User;

        // Get claims of type "role"
        var roleClaims = user.Claims.Where(claim => claim.Type == "role");

        // Check for applicable permission based on role permissions
        // ...
    }

    public Task<bool> IsGrantedAsync(UserIdentifier user, string permissionName)
    {
        return IsGrantedAsync(permissionName);
    }
}

由于 AuthorizationHelper 检查 AbpSession.UserId,您必须重写它的方法。

public class NonUserAuthorizationHelper : AuthorizationHelper
{
    private readonly IAuthorizationConfiguration _authConfiguration

    public NonUserAuthorizationHelper(IFeatureChecker featureChecker, IAuthorizationConfiguration authConfiguration)
        : base(featureChecker, authConfiguration)
    {
        _authConfiguration = authConfiguration;
    }

    public override async Task AuthorizeAsync(IEnumerable<IAbpAuthorizeAttribute> authorizeAttributes)
    {
        if (!_authConfiguration.IsEnabled)
        {
            return;
        }

        // if (!AbpSession.UserId.HasValue)
        // {
        //     throw new AbpAuthorizationException(
        //         LocalizationManager.GetString(AbpConsts.LocalizationSourceName, "CurrentUserDidNotLoginToTheApplication")
        //     );
        // }

        foreach (var authorizeAttribute in authorizeAttributes)
        {
            await PermissionChecker.AuthorizeAsync(authorizeAttribute.RequireAllPermissions, authorizeAttribute.Permissions);
        }
    }
}

然后在 *.Core 模块的 PreInitialize 方法中替换它。

// using Abp.Configuration.Startup;

public override void PreInitialize()
{
    Configuration.ReplaceService<IAuthorizationHelper, NonUserAuthorizationHelper>();
}