Мы пытаемся понять, какова ожидаемая обработка ChallengeResult, когда зарегистрировано несколько схем аутентификации.
Нам нужно разобраться с таким сценарием, потому что у нас есть приложение ASP.NET core 2.2, предоставляющее некоторые методы действий (мы используем промежуточное программное обеспечение MVC), которые должны использоваться Angularjs SPA, который опирается на проверку подлинности куки-файлов и некоторые сторонние приложения, которые использовать механизм аутентификации на основе заголовка HTTP-запроса авторизации. Обратите внимание, что методы задействованных действий одинаковы для обоих пользователей , это означает, что каждый из них должен разрешать аутентификацию с использованием как файла cookie, так и пользовательской схемы, основанной на заголовке HTTP-запроса авторизации. Мы знаем, что, вероятно, это не оптимальный дизайн, но мы не можем изменить общую архитектуру.
Эта документация , кажется, подтверждает, что то, чего мы хотели бы достичь, вполне возможно с использованием ядра ASP.NET 2.2. К сожалению, аутентификация cookie, используемая приложением пользовательского интерфейса, и пользовательская аутентификация, используемая третьими сторонами, должны вести себя по-разному в случае вызова аутентификации, и их ожидаемое поведение несовместимо друг с другом: приложение пользовательского интерфейса должно перенаправить пользователя в форму входа в систему. в то время как стороннее приложение ожидает необработанный ответ кода состояния 401. Приведенная выше документация не дает четкого объяснения обработки ChallengeResult, поэтому мы решили поэкспериментировать с тестовым приложением.
Мы создали два поддельных обработчика аутентификации:
public class FooAuthenticationHandler : IAuthenticationHandler
{
private HttpContext _context;
public Task<AuthenticateResult> AuthenticateAsync()
{
return Task.FromResult(AuthenticateResult.Fail("Foo failed"));
}
public Task ChallengeAsync(AuthenticationProperties properties)
{
_context.Response.StatusCode = StatusCodes.Status403Forbidden;
return Task.CompletedTask;
}
public Task ForbidAsync(AuthenticationProperties properties)
{
return Task.CompletedTask;
}
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
_context = context;
return Task.CompletedTask;
}
}
public class BarAuthenticationHandler : IAuthenticationHandler
{
private HttpContext _context;
public Task<AuthenticateResult> AuthenticateAsync()
{
return Task.FromResult(AuthenticateResult.Fail("Bar failed"));
}
public Task ChallengeAsync(AuthenticationProperties properties)
{
_context.Response.StatusCode = StatusCodes.Status500InternalServerError;
return Task.CompletedTask;
}
public Task ForbidAsync(AuthenticationProperties properties)
{
return Task.CompletedTask;
}
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
_context = context;
return Task.CompletedTask;
}
}
Мы зарегистрировали схемы аутентификации в методе ConfigureServices следующим образом:
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication(options =>
{
options.DefaultChallengeScheme = "Bar";
options.AddScheme<FooAuthenticationHandler>("Foo", "Foo scheme");
options.AddScheme<BarAuthenticationHandler>("Bar", "Bar scheme");
});
}
Это наш промежуточный программный конвейер:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc();
}
и, наконец, мы создали контроллер с методом действия, требующим аутентификации:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values/5
[HttpGet("{id}")]
[Authorize(AuthenticationSchemes = "Foo,Bar")]
public ActionResult<string> Get(int id)
{
return "value";
}
}
Мы заметили, что:
- и
FooAuthenticationHandler
, и BarAuthenticationHandler
вызываются для обработки ChallengeResult
- порядок составляет
FooAuthenticationHandler
до BarAuthenticationHandler
и зависит от атрибута Authorize
(если вы меняете схемы аутентификации внутри атрибута Authorize
, тогда BarAuthenticationHandler
вызывается первым)
- вызывающая сторона получает необработанный ответ с кодом состояния 500, но это зависит только от порядка, в котором называются обработчики авторизации
- вызов
options.DefaultChallengeScheme = "Bar";
имеет значение тогда и только тогда, когда внутри атрибута [Authorize]
свойство AuthenticationSchemes
установлено , а не установлено. Если вы это сделаете, будет вызван только BarAuthenticationHandler
, и FooAuthenticationHandler
никогда не получит шанс аутентифицировать запрос или обработать запрос аутентификации.
Итак, вопрос в основном таков: когда у вас есть такой сценарий, как вы должны справиться с возможной «несовместимостью» различных схем аутентификации, связанных с обработкой ChallengeResult, так как они вызывают оба ?
По нашему мнению, хорошо, что у обоих есть возможность аутентифицировать запрос, но мы хотели бы знать, возможно ли решить, какой из них должен обрабатывать запрос аутентификации.
Спасибо за помощь!