从声明中检查 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>();
}
我正在为 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>();
}