IAuthorizationPolicyProvider:我可以使用多个策略提供者,还是必须在一个通用策略提供者中考虑所有情况?

IAuthorizationPolicyProvider: can I use multiple policy providers or I have to consider all cases inside one general policy provider?

在我的程序中我想

  1. 检查用户是否在特定用户table中注册所有可能的操作。
  2. 只是为了某些操作,我还想检查用户是否有适当的 CRUD 权限。

我使用以下代码检查 startup 中的 第一个条件

services.AddAuthorization(options =>
{
    // This policy checks if a user is registered in our Users table.
    options.AddPolicy(
        "UserIsRegistered",
        new AuthorizationPolicyBuilder()
            .AddRequirements(new RegistrationRequirement())
            .Build());
});

我也加

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers().RequireAuthorization("UserIsRegistered");
});

我相应地定义了 RegistrationRequirementRegistrationAuthorizationHandler

对于 第二个条件 ,我创建了一个属性 CrudAuthorizeAttribute 及其 CrudAuthorizationHandler。由于我对每个可能的 Crud 操作都有不同的警察,因此我创建了一个 CrudPolicyProvider.

然后我在Startup中注册了所有内容:

services.AddTransient<IAuthorizationHandler, RegistrationAuthorizationHandler>();
services.AddTransient<IAuthorizationHandler, CrudAuthorizationHandler>();
services.AddTransient<IAuthorizationPolicyProvider, CrudPolicyProvider>();

现在我看到当我检查第一个条件时,我从 CrudPolicyProvider 获取策略,但我希望此策略生成器仅在第二个条件下提供策略。

因此:

services.AddTransient<IAuthorizationPolicyProvider, FirstPolicyProvider>();
services.AddTransient<IAuthorizationPolicyProvider, SecondPolicyProvider>();

并且只在需要时使用每一个?

谢谢!

can I define two policy providers (as I can do with IAuthorizationHandler)and use each one only when required

你可以。但是两个分离的策略提供者不会被相应地激活。您可能有多个策略提供者,但只会同时使用其中一个。使用 IAuthorizationPolicyProvider 时也没有魔法。

define a single GeneralPolicyProvider .... check which policies I have to provide each time depending on the situation I am? (something like using the POLICY_PREFIX here?).

是的。但是您不必在任何地方都使用 Policy_Prefix。那样你会重复很多次。

更好的方法是在GeneralPolicyProvider中注入一个IHttpContextAccessor服务,这样你就可以在运行-查看当前的EndPoint/HttpContext-时间,然后您可以动态解析目标策略提供程序

一个实现看起来像:

public class GenericPolicyProvider : IAuthorizationPolicyProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private AuthorizationOptions _authZOpts { get; }
    public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
    private IAuthorizationPolicyProvider _fstPolicyProvider  {get;set;}
    private IAuthorizationPolicyProvider _sndPolicyProvider  {get;set;}

    public GenericPolicyProvider(IHttpContextAccessor httpContextAccessor, IOptions<AuthorizationOptions> options)
    {
        this._httpContextAccessor = httpContextAccessor;
        this._authZOpts = options.Value;
        this.FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
        this._fstPolicyProvider = new FirstPolicyProvider(options.Value);
        this._sndPolicyProvider = new SecondPolicyProvider(options.Value);
    }

    // use the target provider to provide policy
    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        var targetPolicyProvider = this.GetPolicyProvider(policyName);
        return targetPolicyProvider.GetPolicyAsync(policyName);
    }

    // get the target provider dynamically
    private IAuthorizationPolicyProvider GetPolicyProvider(string policyName)
    {

        var httpContext = this._httpContextAccessor.HttpContext;
        if(httpContext==null) throw new Exception("HttpContext must not be null");
        // now you get the HttpContext
        //    check HttpContext to determine which policy provider should be used
        // ...
        // or check endpoint,e.g. get a mark filter by endpoint.Metadata.GetMetadata<...>()
        var endpoint = httpContext.GetEndpoint();
        var someMarker = endpoint.Metadata.GetMetadata<SomeMarker>();
        // in short, resolve the policy provider dynamically:
        if(shouldUseFirstPolicyProvider())
            return this._fstPolicyProvider;
        else if(shouldUseSecondPolicyProvider())
            return this._sndPolicyProvider;
        else 
           return this.FallbackPolicyProvider;
    }
    ...
}

最后,别忘了在Startup中注册这个GenericPolicyProvider。