ASP.NET Core 2.0 отключает автоматическую задачу
После обновления моего проекта ASP.NET Core до версии 2.0 попытки доступа к защищенным конечным точкам больше не возвращают 401, а перенаправляются на (несуществующую) конечную точку, пытаясь дать пользователю возможность аутентификации.
Желаемое поведение для приложения просто для возврата 401. Раньше я устанавливал AutomaticChallenge = false
при настройке проверки подлинности, но в соответствии с этой статьей настройка больше не актуальна (на самом деле она больше не существует).
Моя аутентификация настроена следующим образом:
Startup.cs.ConfigureServices():
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(o =>
{
o.Cookie.Name = options.CookieName;
o.Cookie.Domain = options.CookieDomain;
o.SlidingExpiration = true;
o.ExpireTimeSpan = options.CookieLifetime;
o.TicketDataFormat = ticketFormat;
o.CookieManager = new CustomChunkingCookieManager();
});
Настройка():
app.UseAuthentication();
Как отключить автоматический вызов, чтобы приложение вернуло 401, когда пользователь не прошел аутентификацию?
Ответы
Ответ 1
Как указано в некоторых других ответах, больше нет настройки, чтобы отключить автоматический вызов с помощью аутентификации cookie. Решение состоит в переопределении OnRedirectToLogin
:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Events.OnRedirectToLogin = context =>
{
context.Response.Headers["Location"] = context.RedirectUri;
context.Response.StatusCode = 401;
return Task.CompletedTask;
};
});
Это может измениться в будущем: https://github.com/aspnet/Security/issues/1394
Ответ 2
После некоторых исследований я обнаружил, что мы можем справиться с этой проблемой, хотя ниже:
Мы можем добавить две схемы аутентификации как Identity, так и JWT; и использовать схему идентификации для аутентификации и использовать схему JWT для вызова, JWT не будет перенаправляться на любой логин в процессе вызова.
services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();
services.AddAuthentication((cfg =>
{
cfg.DefaultScheme = IdentityConstants.ApplicationScheme;
cfg.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})).AddJwtBearer();
Ответ 3
Согласно эта статья:
В 1.x свойства AutomaticAuthenticate и AutomaticChallenge должны были быть установлены на единой схеме проверки подлинности. Не было хорошего способа обеспечить это.
В 2.0 эти два свойства были удалены как флаги в отдельном экземпляре Аутентификация и переместились в базовый класс AuthenticationOptions. Свойства можно настроить в вызове метода AddAuthentication в ConfigureServices методе Startup.cs
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
В качестве альтернативы используйте перегруженную версию метода AddAuthentication, чтобы установить более одного свойства. В следующем примере перегруженного метода схема по умолчанию установлена в CookieAuthenticationDefaults.AuthenticationScheme. Схема проверки подлинности может быть альтернативно указана в ваших индивидуальных атрибутах [Авторизовать] или политиках авторизации.
services.AddAuthentication(options => {
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
});
Определите схему по умолчанию в 2.0, если выполнено одно из следующих условий:
- Вы хотите, чтобы пользователь автоматически подписался на
- Вы используете атрибут [Авторизовать] или политики авторизации без указания
схемы
Исключением из этого правила является метод AddIdentity. Этот метод добавляет файлы cookie для вас и устанавливает схемы проверки подлинности и вызова по умолчанию в файл cookie приложения IdentityConstants.ApplicationScheme. Кроме того, он устанавливает схему входа по умолчанию для внешнего файла cookie IdentityConstants.ExternalScheme.
Надеюсь, что это поможет вам.
Ответ 4
Совместим с @Serverin, установив OnRedirectToLogin в Cookie приложения, но должен быть выполнен в инструкции следующих служб. AddIdentity в Startup.cs: ConfigureServices:
services.ConfigureApplicationCookie(options => {
options.Events.OnRedirectToLogin = context => {
context.Response.Headers["Location"] = context.RedirectUri;
context.Response.StatusCode = 401;
return Task.CompletedTask;
};
});
Ответ 5
Это исходный код CookieAuthenticationEvents.OnRedirectToLogin:
public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToLogin { get; set; } = context =>
{
if (IsAjaxRequest(context.Request))
{
context.Response.Headers["Location"] = context.RedirectUri;
context.Response.StatusCode = 401;
}
else
{
context.Response.Redirect(context.RedirectUri);
}
return Task.CompletedTask;
};
Вы можете добавить заголовок "X-Requested-With: XMLHttpRequest" в запрос при вызове API от вашего клиента.
Ответ 6
Я не уверен, как сгенерировать ошибку 401, однако, если вы используете:
o.AccessDeniedPath = "{path to invalid}";
Это позволит вам перенаправить куда-нибудь, когда вызов не удался.
Ответ 7
Другой способ сделать это более удобным для DI/тестирования - это использовать AuthenticationSchemeOptions.EventsType
(другой ответ на этот вопрос здесь). Это позволит вам включить другие компоненты в процесс разрешения.
Вот пример, включающий регистрацию и разрешение, которое останавливает перенаправление по умолчанию для входа в систему по неаутентифицированному запросу и вместо этого просто возвращает жесткий 401
. У этого также есть слот для любых других зависимостей, которые могут потребоваться знать о неаутентифицированных запросах.
В Startup.cs
:
services
.AddAuthentication("MyAuthScheme")
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.EventsType = typeof(MyEventsWrapper);
};
...
services.AddTransient<MyEventsWrapper>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Затем в MyEventsWrapper.cs
:
public class MyEventsWrapper : CookieAuthenticationEvents
{
private readonly IHttpContextAccessor _accessor;
private readonly IDependency _otherDependency;
public MyEventsWrapper(IHttpContextAccessor accessor,
IDependency otherDependency)
{
_accessor = accessor;
_otherDependency = otherDependency;
}
public override async Task RedirectToLogin(RedirectContext<CookieAuthenticationOptions> context)
{
context.Response.Headers.Remove("Location");
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await _otherDependency.Cleanup(_accessor.HttpContext);
}
}
Ответ 8
Я обнаружил, что в большинстве случаев решение заключается в переопределении
OnRedirectToLogin
Но в моем приложении я использовал несколько политик аутентификации, и переопределение OnRedirectToLogin
не работало для меня. Решение в моем случае заключалось в добавлении простого промежуточного программного обеспечения для перенаправления входящего запроса.
app.Use(async (HttpContext context, Func<Task> next) => {
await next.Invoke(); //execute the request pipeline
if (context.Response.StatusCode == StatusCodes.Status302Found && context.Response.Headers.TryGetValue("Location", out var redirect)) {
var v = redirect.ToString();
if (v.StartsWith($"{context.Request.Scheme}://{context.Request.Host}/Account/Login")) {
context.Response.Headers["Location"] = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}";
context.Response.StatusCode = 401;
}
}
});