ASP.NET Core中多IAuthorizationPolicyProvider的使用及适配问题
这是个很典型的ASP.NET Core授权策略定制的问题,我来帮你逐个理清思路:
1. 是否可指定CrudPolicyProvider仅配合CrudAttribute使用?
完全可以!ASP.NET Core的IAuthorizationPolicyProvider机制是:当框架需要获取策略时,会先调用你注册的自定义Provider,如果自定义Provider返回null,框架会自动 fallback 到默认的DefaultAuthorizationPolicyProvider。
所以你只需要在CrudPolicyProvider的GetPolicyAsync方法里,判断当前请求的策略名是否属于Crud相关的(比如策略名带有特定前缀,或者是从CrudAuthorizeAttribute生成的专属命名),如果不是就返回null,这样非Crud的策略(比如你的UserIsRegistered)就会交给默认Provider去处理,不会被CrudProvider干扰。
举个具体的代码修改示例:
public class CrudPolicyProvider : IAuthorizationPolicyProvider { private readonly DefaultAuthorizationPolicyProvider _defaultProvider; // 注入默认Provider的依赖 public CrudPolicyProvider(IOptions<AuthorizationOptions> options) { _defaultProvider = new DefaultAuthorizationPolicyProvider(options); } public async Task<AuthorizationPolicy?> GetPolicyAsync(string policyName) { // 只处理Crud相关的策略名(这里假设你的Crud策略名前缀是"Crud_") if (policyName.StartsWith("Crud_", StringComparison.OrdinalIgnoreCase)) { // 解析策略名,生成对应的Crud权限需求 var parts = policyName.Split('_'); var operation = parts[1]; var resource = parts[2]; var requirement = new CrudRequirement(operation, resource); return new AuthorizationPolicyBuilder() .AddRequirements(requirement) .Build(); } // 非Crud策略,交给默认Provider处理(比如UserIsRegistered) return await _defaultProvider.GetPolicyAsync(policyName); } // 实现默认策略和 fallback 策略的获取,直接委托给默认Provider public Task<AuthorizationPolicy> GetDefaultPolicyAsync() { return _defaultProvider.GetDefaultPolicyAsync(); } public Task<AuthorizationPolicy?> GetFallbackPolicyAsync() { return _defaultProvider.GetFallbackPolicyAsync(); } }
这样修改后,CrudPolicyProvider就只会处理自己的Crud策略,全局的UserIsRegistered策略会正常由默认Provider处理。
2. 能否像注册多个IAuthorizationHandler一样注册多个IAuthorizationPolicyProvider并按需使用?
很遗憾,这是不行的。ASP.NET Core的依赖注入系统中,IAuthorizationPolicyProvider是一个“单一服务”(Single Service),也就是说,当你注册多个实现时,最后注册的那个会覆盖之前的。框架在获取策略时,只会调用最后注册的那个Provider,无法像IAuthorizationHandler那样同时存在多个并自动协同工作。
所以你不能直接注册多个Provider来按需使用,必须通过其他方式处理(比如上面提到的在单个Provider里判断策略类型,或者下面说的组合Provider)。
3. 若不可行,是否需实现一个通用PolicyProvider,根据场景判断返回对应策略(比如使用策略前缀)?
是的,这是非常可行且推荐的方案。你可以实现一个组合式PolicyProvider,内部管理多个不同的策略Provider,然后根据策略名的特征(比如前缀)来决定交给哪个子Provider处理。
比如你可以写一个CompositeAuthorizationPolicyProvider:
public class CompositeAuthorizationPolicyProvider : IAuthorizationPolicyProvider { private readonly DefaultAuthorizationPolicyProvider _defaultProvider; private readonly CrudPolicyProvider _crudProvider; public CompositeAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options, CrudPolicyProvider crudProvider) { _defaultProvider = new DefaultAuthorizationPolicyProvider(options); _crudProvider = crudProvider; } public async Task<AuthorizationPolicy?> GetPolicyAsync(string policyName) { // 先尝试用CrudProvider处理 var crudPolicy = await _crudProvider.GetPolicyAsync(policyName); if (crudPolicy != null) { return crudPolicy; } // 否则交给默认Provider return await _defaultProvider.GetPolicyAsync(policyName); } public Task<AuthorizationPolicy> GetDefaultPolicyAsync() { return _defaultProvider.GetDefaultPolicyAsync(); } public Task<AuthorizationPolicy?> GetFallbackPolicyAsync() { return _defaultProvider.GetFallbackPolicyAsync(); } }
然后在Startup里注册这个组合Provider:
services.AddTransient<CrudPolicyProvider>(); services.AddSingleton<IAuthorizationPolicyProvider, CompositeAuthorizationPolicyProvider>();
这种方式的好处是扩展性强,以后如果需要添加其他类型的策略Provider,直接在组合Provider里加入判断逻辑即可,不需要修改原有Provider的代码。
内容的提问来源于stack exchange,提问作者xavier

