Dependency Injection on Authorization Policy

久未见 提交于 2020-06-15 21:36:09

问题


I want to create a claim based authorization for my ASP.NET Core app:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthorization(options =>
    {
        options.AddPolicy("Founders", policy =>
                          policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));
    });
}

The problem is that I have a non trivial method to resolve the employee numbers (1 to 5) and I want to use a DI service:

public interface IEmployeeProvider {

  string[] GetAuthorizedEmployeeIds();
}

I would like to inject this service and use it in AddPolicy, something like:

services.AddAuthorization(options =>
{
    options.AddPolicy("Founders", policy =>
              policy.RequireClaim("EmployeeNumber", *employeeProvider.GetAuthorizedEmployeeIds()));
});

Note

I know that I can write my own AuthorizationHandler where I can easily inject IEmployeeProvider but I'm against this pattern because:

  1. There is a already a handler that does exactly what I need
  2. I need to write a new handler for each claim type and each different requirement
  3. This is an anti pattern because the employee ids should really be part of the requirement while the handler should be generic component that handles the requirements

So I'm looking for a way to inject services when the policy is being built


回答1:


To supplement the provided answer by @MichaelShterenberg, the configuration delegate can use a IServiceProvider to allow for additional dependencies

public static IServiceCollection AddAuthorization(this IServiceCollection services,
    Action<AuthorizationOptions, IServiceProvider> configure) {
    services.AddOptions<AuthorizationOptions>().Configure<IServiceProvider>(configure);
    return services.AddAuthorization();
}

Which, based on the original example, can be used

public void ConfigureServices(IServiceCollection services) {

    //...

    service.AddScoped<IEmployeeProvider, EmployeeProvider>();

    services.AddAuthorization((options, sp) => {
        IEmployeeProvider employeeProvider = sp.GetRequiredService<IEmployeeProvider>();
        options.AddPolicy("Founders", policy =>
            policy.RequireClaim("EmployeeNumber", employeeProvider.GetAuthorizedEmployeeIds())
        );
    });

    //...
}

If there were other dependencies needed, they could be resolved from the service provider.




回答2:


Thanks to Nkosi for the tip!

Since AddAuthorization is basically configuring AuthorizationOptions behind the scenes, I followed the same pattern only I used OptionsBuilder to configure options with dependencies

I created my own AddAuthorization method that accepts dependencies:

 public static IServiceCollection AddAuthorization<TDep>(
     this IServiceCollection services,
     Action<AuthorizationOptions, TDep> configure) where TDep : class
 {
     services.AddOptions<AuthorizationOptions>().Configure<TDep>(configure);
     return services.AddAuthorization();
 }

And now I can use it to properly configure the requirement:

services.AddAuthorization<IEmployeeProvider>((options, employeeProvider> =>
{
    options.AddPolicy("Founders", policy =>
        policy.RequireClaim("EmployeeNumber", employeeProvider.GetAuthorizedEmployeeIds())
    );
});

You can follow the same technique if you need more dependencies (OptionsBuilder.Configure supports up to 5 dependencies)

Obviously, this solution requires extra validation when upgrading to newer ASP versions, as the underlying implementation of AddAuhtorization may change




回答3:


You can build a service provider using the BuildServiceProvider() method on the IServiceCollection:

public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IEmployeeProvider, EmployeeProvider>();

        var sp = services.BuildServiceProvider();
        var employeeProvider = sp.GetService<IEmployeeProvider>();
        string[] values = employeeProvider.GetAuthorizedEmployeeIds();

        services.AddAuthorization(options =>
        {

            options.AddPolicy("Founders", policy =>
                      policy.RequireClaim("EmployeeNumber", employeeProvider.GetAuthorizedEmployeeIds()));
        });
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

interface and Class

public interface IEmployeeProvider
{
    string[] GetAuthorizedEmployeeIds();
}

public class EmployeeProvider : IEmployeeProvider
{
    public string[] GetAuthorizedEmployeeIds()
    {
        var data = new string[] { "1", "2", "3", "4", "5" };
        return data;
    }
}


来源:https://stackoverflow.com/questions/59083989/dependency-injection-on-authorization-policy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!