如何创建属性以检查用户是否拥有 Identity core 2.2 的声明?
How can I create attribute to check if the user has a claim with Identity core 2.2?
我在 ASP.NET Core 2.2 框架的顶部使用 C#
编写了一个应用程序。
我希望能够在允许用户访问该操作之前检查他们是否有声明。
我创建了一个 AuthorizationHandler
来检查用户是否有这样的声明
public class ClaimExistanceHandler : AuthorizationHandler<MustHaveClaimRequirement>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MustHaveClaimRequirement requirement)
{
if (context == null
|| context.User == null
|| context.User.Identity == null
|| !context.User.Identity.IsAuthenticated
|| requirement == null
|| string.IsNullOrWhiteSpace(requirement.Type)
|| context.User.HasClaim(requirement.Type, requirement.Value))
{
context.Fail();
}
else
{
context.Succeed(requirement);
}
await Task.Yield();
}
}
}
那么要求如下
public class MustHaveClaimRequirement : IAuthorizationRequirement
{
public string Type { get; set; }
public string Value { get; set; }
public MustHaveClaimRequirement(string type, string value)
{
Type = type;
Value = value;
}
}
但是如何将此要求称为属性?例如HasPermission("do something", "1")
似乎我的 HasPermission class 需要实现 AuthorizeAttribute
但不确定如何从属性调用处理程序。
您的主要目标是将要求纳入政策,然后使用或创建一个属性,该属性可以用字符串名称指定该政策。完成后,无需担心自己调用处理程序,因为 ASP.NET Core 会为您处理。
创建策略的最简单方法是在应用程序启动时创建,如文档所述here。您创建策略,然后使用 AuthorizeAttribute
指定要附加到每个端点的策略。
但是,这样做需要您预先定义所有策略。如果这会导致您需要创建大量不同的政策(因为您将要检查许多不同的索赔类型),而您真正想要的是能够拥有一个指定声明信息的属性,还有一种更动态的方法:请参阅 here。您需要创建一个 AuthorizeAttribute
实现来将您的参数值(名称和类型)填充到一个字符串中,然后创建并注册一个 IAuthorizationPolicyProvider
来解释该字符串并生成具有适当要求的策略。
编辑:还值得指出的是 ASP.NET Core 已经包含一个用于检查声明的需求实现:ClaimsAuthorizationRequirement
。 AuthorizationPolicyBuilder
有一个快捷方式 (RequireClaim
),因此您可以快速创建检查声明的策略:
services.AddAuthorization(options =>
{
options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
});
首先,您需要注册保单及其相关索赔:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization(options =>
{
//Scenario 0: Policy requires Claim0 without care what the value is
options.AddPolicy("MyPolicy0", policy => policy.RequireClaim("Claim0"));
//Scenario 1: Policy requires Claim1 with value ClaimValue1_1 OR ClaimValue 1_2
options.AddPolicy("MyPolicy1", policy => policy.RequireClaim("Claim1", "ClaimValue1_1", "ClaimValue1_2"));
//Scenario 2: Policy requires Claims2 AND Claim3 with particular values
options.AddPolicy("MyPolicy2", policy => {
policy.RequireClaim("Claim2", "ClaimValue2");
policy.RequireClaim("Claim3", "ClaimValue3"));
}
//Scenario 3: Policy requires Claims4 OR Claim5 with particular values
options.AddPolicy("MyPolicy3", policy => {
policy.RequireAssertion(ctx =>
{
return ctx.User.HasClaim("Claim4", "ClaimValue4") ||
ctx.User.HasClaim("Claim5", "ClaimValue5");
})
}
});
}
然后在需要时应用这些检查(可以在控制器或操作级别应用):
[Authorize(Policy = "Policy1")]
public class HomeController : Controller
{
[Authorize(Policy = "Policy2")]
public ActionResult MyAction()
{
...
}
[Authorize(Policy = "Policy3")]
public ActionResult MyAnotherAction()
{
...
}
[AllowAnonymous]
public ActionResult NotSecuredAtAll()
{
...
}
}
不要忘记,如果您将多个策略应用于一个控制器或操作,那么所有策略都必须通过才能授予访问权限(如果它们没有 AllowAnonymous
属性)。
在从 nlawalker
和 ivamax9
获得反馈后,我设法编写了允许检查声明是否存在的属性
总而言之,HasPermissionAttribute
class 采用 claimType
和可选的 claimValue
然后创建策略名称。属性 class 看起来像这样
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class HasPermissionAttribute : AuthorizeAttribute
{
public const string Policy_Prefix = "HasClaim";
public const string Policy_Glue = ".";
public HasPermissionAttribute(string type, string value = null)
{
Policy = GetPolicyValue(type, value);
}
private string GetPolicyValue(string type, string value)
{
if (string.IsNullOrWhiteSpace(type))
{
throw new ArgumentNullException($"{type} cannot be null.");
}
List<string> parts = new List<string> { type.Replace(Policy_Glue, "_").Trim() };
if (!string.IsNullOrWhiteSpace(value))
{
parts.Add(value.Replace(Policy_Glue, "_"));
}
string policy = $"{Policy_Prefix}{Policy_Glue}{string.Join(Policy_Glue, parts)}";
return policy;
}
}
现在我们有一个通过 HasPermissionAttribute
应用的策略,我们现在需要采用应用的策略并使用 AuthorizationPolicyBuilder
注册它,检查给定的声明是否存在。也就是说,我添加了一个名为 ClaimCheckerPolicyProvider
的 class,它接受提供的声明并按以下方式处理支票
internal class ClaimCheckerPolicyProvider : IAuthorizationPolicyProvider
{
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
{
return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
}
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
if (IsClaimBasePolicy(policyName))
{
string[] parts = GetParts(policyName);
if (parts.Length > 0)
{
AuthorizationPolicyBuilder policy = GetPolicyBuilder(parts);
return Task.FromResult(policy.Build());
}
}
return Task.FromResult<AuthorizationPolicy>(null);
}
private bool IsClaimBasePolicy(string policyName)
{
return !string.IsNullOrWhiteSpace(policyName)
&& policyName.StartsWith(HasPermissionAttribute.Policy_Prefix, StringComparison.OrdinalIgnoreCase);
}
private string[] GetParts(string policyName)
{
return policyName.Split(HasPermissionAttribute.Policy_Glue, StringSplitOptions.RemoveEmptyEntries)
.Where(x => !x.Equals(HasPermissionAttribute.Policy_Prefix))
.ToArray();
}
private AuthorizationPolicyBuilder GetPolicyBuilder(string[] parts)
{
if (parts == null)
{
throw new ArgumentNullException($"{nameof(parts)} cannot be null.");
}
var length = parts.Length;
if (length == 0)
{
throw new ArgumentOutOfRangeException($"{nameof(parts)} cannot cannot be empty.");
}
var policy = new AuthorizationPolicyBuilder();
if (length > 1)
{
return policy.RequireClaim(parts[0], parts[1]);
}
return policy.RequireClaim(parts[0]);
}
}
最后,我们需要将提供程序注册为服务。在Startupclass的ConfigureServices
中,我们添加如下
services.AddTransient<IAuthorizationPolicyProvider, ClaimCheckerPolicyProvider>();
我在 ASP.NET Core 2.2 框架的顶部使用 C#
编写了一个应用程序。
我希望能够在允许用户访问该操作之前检查他们是否有声明。
我创建了一个 AuthorizationHandler
来检查用户是否有这样的声明
public class ClaimExistanceHandler : AuthorizationHandler<MustHaveClaimRequirement>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MustHaveClaimRequirement requirement)
{
if (context == null
|| context.User == null
|| context.User.Identity == null
|| !context.User.Identity.IsAuthenticated
|| requirement == null
|| string.IsNullOrWhiteSpace(requirement.Type)
|| context.User.HasClaim(requirement.Type, requirement.Value))
{
context.Fail();
}
else
{
context.Succeed(requirement);
}
await Task.Yield();
}
}
}
那么要求如下
public class MustHaveClaimRequirement : IAuthorizationRequirement
{
public string Type { get; set; }
public string Value { get; set; }
public MustHaveClaimRequirement(string type, string value)
{
Type = type;
Value = value;
}
}
但是如何将此要求称为属性?例如HasPermission("do something", "1")
似乎我的 HasPermission class 需要实现 AuthorizeAttribute
但不确定如何从属性调用处理程序。
您的主要目标是将要求纳入政策,然后使用或创建一个属性,该属性可以用字符串名称指定该政策。完成后,无需担心自己调用处理程序,因为 ASP.NET Core 会为您处理。
创建策略的最简单方法是在应用程序启动时创建,如文档所述here。您创建策略,然后使用 AuthorizeAttribute
指定要附加到每个端点的策略。
但是,这样做需要您预先定义所有策略。如果这会导致您需要创建大量不同的政策(因为您将要检查许多不同的索赔类型),而您真正想要的是能够拥有一个指定声明信息的属性,还有一种更动态的方法:请参阅 here。您需要创建一个 AuthorizeAttribute
实现来将您的参数值(名称和类型)填充到一个字符串中,然后创建并注册一个 IAuthorizationPolicyProvider
来解释该字符串并生成具有适当要求的策略。
编辑:还值得指出的是 ASP.NET Core 已经包含一个用于检查声明的需求实现:ClaimsAuthorizationRequirement
。 AuthorizationPolicyBuilder
有一个快捷方式 (RequireClaim
),因此您可以快速创建检查声明的策略:
services.AddAuthorization(options =>
{
options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
});
首先,您需要注册保单及其相关索赔:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization(options =>
{
//Scenario 0: Policy requires Claim0 without care what the value is
options.AddPolicy("MyPolicy0", policy => policy.RequireClaim("Claim0"));
//Scenario 1: Policy requires Claim1 with value ClaimValue1_1 OR ClaimValue 1_2
options.AddPolicy("MyPolicy1", policy => policy.RequireClaim("Claim1", "ClaimValue1_1", "ClaimValue1_2"));
//Scenario 2: Policy requires Claims2 AND Claim3 with particular values
options.AddPolicy("MyPolicy2", policy => {
policy.RequireClaim("Claim2", "ClaimValue2");
policy.RequireClaim("Claim3", "ClaimValue3"));
}
//Scenario 3: Policy requires Claims4 OR Claim5 with particular values
options.AddPolicy("MyPolicy3", policy => {
policy.RequireAssertion(ctx =>
{
return ctx.User.HasClaim("Claim4", "ClaimValue4") ||
ctx.User.HasClaim("Claim5", "ClaimValue5");
})
}
});
}
然后在需要时应用这些检查(可以在控制器或操作级别应用):
[Authorize(Policy = "Policy1")]
public class HomeController : Controller
{
[Authorize(Policy = "Policy2")]
public ActionResult MyAction()
{
...
}
[Authorize(Policy = "Policy3")]
public ActionResult MyAnotherAction()
{
...
}
[AllowAnonymous]
public ActionResult NotSecuredAtAll()
{
...
}
}
不要忘记,如果您将多个策略应用于一个控制器或操作,那么所有策略都必须通过才能授予访问权限(如果它们没有 AllowAnonymous
属性)。
在从 nlawalker
和 ivamax9
总而言之,HasPermissionAttribute
class 采用 claimType
和可选的 claimValue
然后创建策略名称。属性 class 看起来像这样
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class HasPermissionAttribute : AuthorizeAttribute
{
public const string Policy_Prefix = "HasClaim";
public const string Policy_Glue = ".";
public HasPermissionAttribute(string type, string value = null)
{
Policy = GetPolicyValue(type, value);
}
private string GetPolicyValue(string type, string value)
{
if (string.IsNullOrWhiteSpace(type))
{
throw new ArgumentNullException($"{type} cannot be null.");
}
List<string> parts = new List<string> { type.Replace(Policy_Glue, "_").Trim() };
if (!string.IsNullOrWhiteSpace(value))
{
parts.Add(value.Replace(Policy_Glue, "_"));
}
string policy = $"{Policy_Prefix}{Policy_Glue}{string.Join(Policy_Glue, parts)}";
return policy;
}
}
现在我们有一个通过 HasPermissionAttribute
应用的策略,我们现在需要采用应用的策略并使用 AuthorizationPolicyBuilder
注册它,检查给定的声明是否存在。也就是说,我添加了一个名为 ClaimCheckerPolicyProvider
的 class,它接受提供的声明并按以下方式处理支票
internal class ClaimCheckerPolicyProvider : IAuthorizationPolicyProvider
{
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
{
return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
}
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
if (IsClaimBasePolicy(policyName))
{
string[] parts = GetParts(policyName);
if (parts.Length > 0)
{
AuthorizationPolicyBuilder policy = GetPolicyBuilder(parts);
return Task.FromResult(policy.Build());
}
}
return Task.FromResult<AuthorizationPolicy>(null);
}
private bool IsClaimBasePolicy(string policyName)
{
return !string.IsNullOrWhiteSpace(policyName)
&& policyName.StartsWith(HasPermissionAttribute.Policy_Prefix, StringComparison.OrdinalIgnoreCase);
}
private string[] GetParts(string policyName)
{
return policyName.Split(HasPermissionAttribute.Policy_Glue, StringSplitOptions.RemoveEmptyEntries)
.Where(x => !x.Equals(HasPermissionAttribute.Policy_Prefix))
.ToArray();
}
private AuthorizationPolicyBuilder GetPolicyBuilder(string[] parts)
{
if (parts == null)
{
throw new ArgumentNullException($"{nameof(parts)} cannot be null.");
}
var length = parts.Length;
if (length == 0)
{
throw new ArgumentOutOfRangeException($"{nameof(parts)} cannot cannot be empty.");
}
var policy = new AuthorizationPolicyBuilder();
if (length > 1)
{
return policy.RequireClaim(parts[0], parts[1]);
}
return policy.RequireClaim(parts[0]);
}
}
最后,我们需要将提供程序注册为服务。在Startupclass的ConfigureServices
中,我们添加如下
services.AddTransient<IAuthorizationPolicyProvider, ClaimCheckerPolicyProvider>();