.NET Core multi аутентификации с авторизацией провайдера динамических политик - PullRequest
0 голосов
/ 27 мая 2019

.NET Core 2.0 API:

Сначала я попытался использовать IAuthorizationPolicyProvider, чтобы иметь динамические политики:

public class MyAuthorizationPolicyProvider: IAuthorizationPolicyProvider
{
    internal const string PolicyPrefix = "MyJwt";

    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        return Task.FromResult<AuthorizationPolicy>(new AuthorizationPolicyBuilder(Array.Empty<string>()).RequireAuthenticatedUser().Build());
    }

    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        if(!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
            return Task.FromResult<AuthorizationPolicy>((AuthorizationPolicy)null);

        var authorizationPolicyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
        authorizationPolicyBuilder.AddRequirements((IAuthorizationRequirement)new MyAuthorizationRequirement(policyName.Substring(PolicyPrefix.Length)));
        return Task.FromResult<AuthorizationPolicy>(authorizationPolicyBuilder.Build());
    }
}



public class MyAuthorizationHandler: AuthorizationHandler<MyAuthorizationRequirement>
{

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyAuthorizationRequirement requirement)
    {
        if(!context.User.Identity.IsAuthenticated)
        {
            context.Fail();
            return Task.CompletedTask;
        }



        if(SomeLibrary.IsContextValid(context, requirement))
            context.Succeed((IAuthorizationRequirement)requirement);
        else
            context.Fail();

        return Task.CompletedTask;
    }
}

и добавить аутентификацию:

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

    }).AddJwtBearer(options =>
    {
        options.TokenValidationParameters = GetTokenValidationParameters();
    });

    services.AddSingleton<IAuthorizationHandler, MyAuthorizationHandler>();
    services.AddSingleton<IAuthorizationPolicyProvider, MyAuthorizationPolicyProvider>();

И мой атрибут авторизации:

public class MyAuthorizeAttribute: AuthorizeAttribute
{

    internal const string PolicyPrefix = "MyJwt";

    public MyAuthorizeAttribute(string permission)
    {
        this.Permission = permission;
    }

    public string Permission
    {
        get
        {
            if(this.Policy.Length < PolicyPrefix.Length)
                return (string)null;
            return this.Policy.Substring(PolicyPrefix.Length);
        }
        set
        {
            this.Policy = PolicyPrefix + value;
        }
    }
}

И я использовал: [MyAuthorize("anythings")]

Пока все было хорошо. Теперь мне нужно добавить новую аутентификацию (я имею в виду, что оба работают вместе).

Поэтому я изменил MyAuthorizationPolicyProvider для поддержки новых политик:

public class MyAuthorizationPolicyProvider: IAuthorizationPolicyProvider
    {
        internal const string PolicyPrefix = "MyJwt";

        public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
        {
            return Task.FromResult<AuthorizationPolicy>(new AuthorizationPolicyBuilder(Array.Empty<string>()).RequireAuthenticatedUser().Build());
        }

        public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
        {

             if(policyName == "newpolicy") //here
                return AddNewPolicy();


            if(!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
                return Task.FromResult<AuthorizationPolicy>((AuthorizationPolicy)null);

            var authorizationPolicyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
            authorizationPolicyBuilder.AddRequirements((IAuthorizationRequirement)new MyAuthorizationRequirement(policyName.Substring(PolicyPrefix.Length)));
            return Task.FromResult<AuthorizationPolicy>(authorizationPolicyBuilder.Build());
        }
    }

И изменить Добавить аутентификацию:

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

    }).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.TokenValidationParameters = GetTokenValidationParameters();
    })
    .AddJwtBearer("newSchema", options =>
    {
        options.TokenValidationParameters = GetNewSchemaTokenValidationParameters();
    });

    services.AddSingleton<IAuthorizationHandler, MyAuthorizationHandler>();
    services.AddSingleton<IAuthorizationHandler, MyNewAuthorizationHandler>();
    services.AddSingleton<IAuthorizationPolicyProvider, MyAuthorizationPolicyProvider>();

А MyNewAuthorizationHandler - это:

public class MyNewAuthorizationHandler: AuthorizationHandler<MyNewAuthorizationRequirement>
{

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyNewAuthorizationRequirement requirement)
    {
        if(!context.User.Identity.IsAuthenticated)
        {
            context.Fail();
            return Task.CompletedTask;
        }



        if(SomeNewLibrary.IsContextValid(context, requirement))
            context.Succeed((IAuthorizationRequirement)requirement);
        else
            context.Fail();

        return Task.CompletedTask;
    }
}

а я пользуюсь [Authorize(Policy = "newpolicy")]

Теперь Первая проверка подлинности работает нормально с MyAuthorizationHandler, но вторая проверка подлинности не удалась в MyNewAuthorizationHandler, поскольку пользователь не прошел проверку подлинности и возврат этой строки завершился неудачно: if(!context.User.Identity.IsAuthenticated)

Что я пропустил?

Как я могу иметь множественную аутентификацию и в то же время использовать IAuthorizationPolicyProvider для динамического добавления политик?

Я знаю, если я изменю AddAuthentication на:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = "newSchema"; //here
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.TokenValidationParameters = GetTokenValidationParameters();
})
.AddJwtBearer("newSchema", options =>
{
    options.TokenValidationParameters = GetNewSchemaTokenValidationParameters();
});

Тогда второй работает нормально, а первый не работает. Как я могу сделать аутентификацию обеих схем?

1 Ответ

0 голосов
/ 28 мая 2019

После некоторого поиска я нашел следующее: множественные поставщики политик авторизации .

Первое изменение MyAuthorizationPolicyProvider для поддержки всех других политик:

public class MyAuthorizationPolicyProvider: IAuthorizationPolicyProvider
{
    private readonly DefaultAuthorizationPolicyProvider _fallbackPolicyProvider;
    internal const string PolicyPrefix = "MyJwt";

    public MyAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options)
    {
        _fallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
    }


    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        return Task.FromResult<AuthorizationPolicy>(new AuthorizationPolicyBuilder(Array.Empty<string>()).RequireAuthenticatedUser().Build());
    }

    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {

        if(!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
            return _fallbackPolicyProvider.GetPolicyAsync(policyName); //here return back to static policies

        var authorizationPolicyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
        authorizationPolicyBuilder.AddRequirements((IAuthorizationRequirement)new MyAuthorizationRequirement(policyName.Substring(PolicyPrefix.Length)));
        return Task.FromResult<AuthorizationPolicy>(authorizationPolicyBuilder.Build());
    }
}

Затем измените способ добавления аутентификаций:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.TokenValidationParameters = GetTokenValidationParameters();
})
.AddJwtBearer("newSchema", options =>
{
    options.TokenValidationParameters = GetNewSchemaTokenValidationParameters();
});



services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes("newSchema") //add new schemas here
        .Build();

    //here: add static policies as much as you want   
    options.AddPolicy("newpolicy", new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes("newSchema")
        .AddRequirements(new MyNewAuthorizationRequirement("newpolicy"))
        .Build());

});

services.AddSingleton<IAuthorizationHandler, MyAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, MyNewAuthorizationHandler>();
services.AddSingleton<IAuthorizationPolicyProvider, MyAuthorizationPolicyProvider>();
...