API .NET Core 2.0:
Сначала я попытался использовать 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();
});
Тогда второй работает нормально, а первый не работает. Как я могу аутентифицировать обе схемы?
После некоторого поиска вот что я нашел: поставщики политики множественной авторизации.
Первое изменение 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>();