#c# #asp.net-mvc #asp.net-core
#c# #asp.net-mvc #asp.net-core
Вопрос:
Мой ASP.В веб-приложении Net Core 2.2 есть некоторые контроллеры, которые возвращают View
(ы) и некоторые контроллеры, которые возвращают результат json, поскольку они являются просто контроллерами API.
Мой веб-сайт использует аутентификацию cookie с помощью этого объявления:
services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.AccessDeniedPath = "/Pages/AccessDenied";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Strict;
});
В Configuration
разделе:
// ...
app.UseAuthentication();
app.UseDefaultFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Pages}/{action=Index}/{id?}");
});
Мой PagesController
просто наследуется от Microsoft.AspNetCore.Mvc.Controller
и для некоторых страниц я помещаю [Authorize]
атрибут. Когда неавторизованный пользователь пытается открыть страницу, он корректно перенаправляется на мою общедоступную AccessDenied
страницу. Все хорошо.
НО у меня также есть an ApiController
, который наследуется от Microsoft.AspNetCore.Mvc.ControllerBase
, имеет атрибут [ApiController]
, а также a [Authorize(Roles = "Administrator")]
. Я использую этот контроллер для связи с javascript на своих страницах. Проблема в том, что когда неавторизованный пользователь пытается вызвать методы на этом контроллере, я не хочу, чтобы он отвечал страницей HTTP 200 (той AccessDenied
самой), но я хочу, чтобы он возвращал только HTTP 401, неавторизованный, потому что это API.
Как я могу добиться такого другого поведения, сохраняя аутентификацию cookie?
Спасибо!
Комментарии:
1. по сути, вы хотите, чтобы, когда несанкционированный вызов AJAX достигал одного из ваших контроллеров API, HTTP-ответ представлял собой необработанный код состояния 401, верно? (вместо перенаправления 302 на страницу входа или страницу отказа в доступе)
2. Используете ли вы контроллеры API только для вызовов AJAX, поступающих с ваших интерфейсных страниц?
3. Да на оба вопроса!
Ответ №1:
Самый простой способ добиться такого поведения — написать пользовательский класс, который является производным от CookieAuthenticationEvents и переопределяет как RedirectToLogin, так и RedirectToAccessDenied .
Если вы не знакомы с CookieAuthenticationEvents
классом, вы можете начать с этого примера в документации. По сути, это точка подключения для добавления настраиваемого поведения для аутентификации cookie.
Прежде всего, вам нужен способ идентифицировать ваши запросы AJAX. Самый простой способ сделать это — использовать общий префикс в пути запроса для всех ваших запросов AJAX. В качестве примера вы можете решить, что все ваши пути запросов AJAX будут начинаться с /api
.
Вы можете написать следующий пользовательский CookieAuthenticationEvents
класс:
public sealed class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
public override Task RedirectToAccessDenied(
RedirectContext<CookieAuthenticationOptions> context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (IsAjaxRequest(context.HttpContext))
{
context.Response.StatusCode = StatusCodes.Status403Forbidden;
return Task.CompletedTask;
}
else
{
// non AJAX requests behave as usual
return base.RedirectToAccessDenied(context);
}
}
public override Task RedirectToLogin(RedirectContext<CookieAuthenticationOptions> context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (IsAjaxRequest(context.HttpContext))
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
}
else
{
// non AJAX requests behave as usual
return base.RedirectToLogin(context);
}
}
private static bool IsAjaxRequest(HttpContext httpContext)
{
var requestPath = httpContext.Request.Path;
return requestPath.StartsWithSegments(new PathString("/api"), StringComparison.OrdinalIgnoreCase);
}
}
Теперь вы можете зарегистрировать свой пользовательский CookieAuthenticationEvents
интерфейс в службах аутентификации внутри вашего Startup.ConfigureServices
метода:
services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.AccessDeniedPath = "/Pages/AccessDenied";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Strict;
options.EventsType = typeof(CustomCookieAuthenticationEvents);
});
services.AddSingleton<CustomCookieAuthenticationEvents>();
Комментарии:
1. Вы также можете просто подписаться на события, используя предоставленное свойство Events следующим образом:
options.Events.RedirectToLogin = context => { context.Response.StatusCode = 401; return Task.CompletedTask; }