作者:Mike Rousos
通常,在使用基于策略的授权时,通过调用 AuthorizationOptions.AddPolicy
作为授权服务配置的一部分来注册策略。 在某些情况下,可能无法(或需要)以这种方式注册所有的授权策略。 在这些情况下,可以使用自定义 IAuthorizationPolicyProvider
来控制如何提供授权策略。
自定义IAuthorizationPolicyProvider可能有用的方案示例包括:
AuthorizationOptions.AddPolicy
调用并无意义。从AspNetCore GitHub 存储库查看或下载示例代码。 下载 dotnet/AspNetCore 存储库 ZIP 文件。 解压缩文件。 导航到src/Security/samples/CustomPolicyProvider项目文件夹。
ASP.NET Core 应用使用的实现IAuthorizationPolicyProvider
接口以检索授权策略。 默认情况下, DefaultAuthorizationPolicyProvider已注册并使用。 DefaultAuthorizationPolicyProvider
返回 IServiceCollection.AddAuthorization
调用中提供的 AuthorizationOptions
的策略。
自定义此行为,方法是在应用程序的依赖关系注入容器中注册不同的 IAuthorizationPolicyProvider
实现。
IAuthorizationPolicyProvider
接口包含三个 Api:
[Authorize]
属性的策略)。通过实现这些 Api,你可以自定义如何提供授权策略。
IAuthorizationPolicyProvider
很有用的一种情况是,启用其要求依赖于参数的自定义 [Authorize]
特性。 例如,在基于策略的授权文档中,使用基于时期的("AtLeast21")策略作为示例。 如果应用中的不同控制器操作应该在不同年龄段的用户中可用,则使用许多不同的基于时期的策略可能会很有用。 您可以使用自定义 IAuthorizationPolicyProvider
动态生成策略,而不是注册应用程序 AuthorizationOptions
所需的所有基于时期的不同策略。 若要更轻松地使用策略,可以使用自定义授权属性(如 [MinimumAgeAuthorize(20)]
)来批注操作。
授权策略由其名称标识。 前面所述的自定义 MinimumAgeAuthorizeAttribute
需要将参数映射到一个字符串,该字符串可用于检索相应的授权策略。 为此,可以从 AuthorizeAttribute
派生,并使 Age
属性包装 AuthorizeAttribute.Policy
属性。
internal class MinimumAgeAuthorizeAttribute : AuthorizeAttribute { const string POLICY_PREFIX = "MinimumAge"; public MinimumAgeAuthorizeAttribute(int age) => Age = age; // Get or set the Age property by manipulating the underlying Policy property public int Age { get { if (int.TryParse(Policy.Substring(POLICY_PREFIX.Length), out var age)) { return age; } return default(int); } set { Policy = $"{POLICY_PREFIX}{value.ToString()}"; } } }
此属性类型具有基于硬编码前缀("MinimumAge"
)的 Policy
字符串,以及通过构造函数传入的整数。
可以采用与其他 Authorize
特性相同的方式将其应用于操作,只不过它采用整数作为参数。
[MinimumAgeAuthorize(10)] public IActionResult RequiresMinimumAge10()
使用自定义 MinimumAgeAuthorizeAttribute
可以轻松地请求所需的最短期限的授权策略。 要解决的下一个问题是确保授权策略可用于所有这些不同年龄段。 这就是 IAuthorizationPolicyProvider
有用的地方。
使用 MinimumAgeAuthorizationAttribute
时,授权策略名称将遵循模式 "MinimumAge" + Age
,因此自定义 IAuthorizationPolicyProvider
应通过以下方式生成授权策略:
AuthorizationPolicyBuilder
创建新 AuthorizationPolicy
AuthorizationPolicyBuilder
或始终成功。 否则,没有关于如何向用户提供质询的信息,将引发异常。AuthorizationPolicyBuilder.AddRequirements
的年龄将要求添加到策略。 在其他情况下,你可能会改用 RequireClaim
、RequireRole
或 RequireUserName
。internal class MinimumAgePolicyProvider : IAuthorizationPolicyProvider { const string POLICY_PREFIX = "MinimumAge"; // Policies are looked up by string name, so expect 'parameters' (like age) // to be embedded in the policy names. This is abstracted away from developers // by the more strongly-typed attributes derived from AuthorizeAttribute // (like [MinimumAgeAuthorize()] in this sample) public Task<AuthorizationPolicy> GetPolicyAsync(string policyName) { if (policyName.StartsWith(POLICY_PREFIX, StringComparison.OrdinalIgnoreCase) && int.TryParse(policyName.Substring(POLICY_PREFIX.Length), out var age)) { var policy = new AuthorizationPolicyBuilder(CookieAuthenticationDefaults.AuthenticationScheme); policy.AddRequirements(new MinimumAgeRequirement(age)); return Task.FromResult(policy.Build()); } return Task.FromResult<AuthorizationPolicy>(null); } }
使用自定义时IAuthorizationPolicyProvider
实现中,请记住,ASP.NET Core 仅使用的一个实例IAuthorizationPolicyProvider
。 如果自定义提供程序无法为将使用的所有策略名称提供授权策略,则该提供程序应遵从备份提供程序。
例如,假设某个应用程序需要自定义年龄策略和更传统的基于角色的策略检索。 此类应用程序可使用自定义授权策略提供程序,该提供程序:
DefaultAuthorizationPolicyProvider
)。可以通过在其构造函数中创建备份策略提供程序来更新上面所示的示例 IAuthorizationPolicyProvider
实现使用 DefaultAuthorizationPolicyProvider
(如果策略名称与 "MinimumAge" + age 的预期模式不符,则使用此方法)。
private DefaultAuthorizationPolicyProvider BackupPolicyProvider { get; } public MinimumAgePolicyProvider(IOptions<AuthorizationOptions> options) { // ASP.NET Core only uses one authorization policy provider, so if the custom implementation // doesn't handle all policies it should fall back to an alternate provider. BackupPolicyProvider = new DefaultAuthorizationPolicyProvider(options); }
然后,可以将 GetPolicyAsync
方法更新为使用 BackupPolicyProvider
,而不是返回 null:
... return BackupPolicyProvider.GetPolicyAsync(policyName);
自定义 IAuthorizationPolicyProvider
除了提供命名的授权策略外,还需要实现 GetDefaultPolicyAsync
,以提供 [Authorize]
属性的授权策略,而无需指定策略名称。
在许多情况下,此授权特性只需要经过身份验证的用户,因此,你可以通过调用 RequireAuthenticatedUser
来执行必要的策略:
public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => Task.FromResult(new AuthorizationPolicyBuilder(CookieAuthenticationDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build());
与自定义 IAuthorizationPolicyProvider
的所有方面一样,你可以根据需要对其进行自定义。 在某些情况下,可能需要从回退 IAuthorizationPolicyProvider
检索默认策略。
自定义 IAuthorizationPolicyProvider
可以选择实现 GetFallbackPolicyAsync
来提供在结合策略时使用的策略,以及未指定策略时使用的策略。 如果 GetFallbackPolicyAsync
返回非 null 策略,则在未为请求指定策略时,授权中间件会使用返回的策略。
如果不需要回退策略,则提供程序可以返回 null
或将其推迟到回退提供程序:
public Task<AuthorizationPolicy> GetFallbackPolicyAsync() => Task.FromResult<AuthorizationPolicy>(null);
若要从 IAuthorizationPolicyProvider
使用自定义策略,你必须:
AuthorizationHandler
类型(如基于策略的授权中所述)。IAuthorizationPolicyProvider
类型(在 Startup.ConfigureServices
中)以替换默认策略提供程序。services.AddSingleton<IAuthorizationPolicyProvider, MinimumAgePolicyProvider>();
Aspnet/AuthSamples GitHub 存储库中提供了一个完整的自定义 IAuthorizationPolicyProvider
示例。