CORS. Использование Ресурсов Между Разными Источниками. Окончание
Начало
Продолжение
Теперь, когда политика настроена, пришло время её использовать. В зависимости от приложения, возможно, вы захотите, чтобы она применялась глобально, только для определённых конечных точек или маршрутов.
1. Глобально
Нужна политика по умолчанию, затем нужно добавить вызов метода UseCors(). Важен порядок. CORS необходимо активировать:
- после маршрутизации (UseRouting()),
- перед кешированием ответов, аутентификацией и авторизацией (UseResponseCaching(), UseAuthentication(), UseAuthorization()).
Для политики по умолчанию достаточно вызвать:
app.UseCors();
2. Для конечных точек
Для именованных политик также нужен вызов UseCors() в Program.cs, но т.к. нет политики по умолчанию, затем необходимо добавить атрибут [EnableCors] с именем политики в класс контроллера (для всех методов) или только в отдельные методы:
[Route("api/[controller]")]
[EnableCors("CORS API Endpoint")]
[ApiController]
public class CorsController : ControllerBase
{
…
}Теперь все методы в контроллере API отправляют правильный заголовок Access-Control-Allow-Origin. При этом атрибут [DisableCors] отключит политику для отдельных методов контроллера. Применив эти изменения к API и снова вызвав страницу из изначального примера, мы получаем желаемый результат. На этот раз не только проходит HTTP-запрос, но и код JS может получить доступ к возвращаемому значению и отобразить его.
3. Для маршрутов
Применяе именованные политики к маршрутам. В ASP.NET Core это настраивается в методе app.UseEndpoints() с помощью расширения RequireCors() после вызова MapControllers(), MapControllerRoute(), MapGet() или аналогичных:
app.UseEndpoints(eps =>
{
eps.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.RequireCors("CORS API Endpoint");
});
CORS на стероидах: предварительные запросы
GET-запрос из нашего примера всё равно выполняется, даже если не соответствует политике (т.к. считается, что GET-запросы не изменяют состояния приложения), но код JS не получает доступа к возвращаемым данным. POST (и другие) запросы могут изменить состояние приложения, поэтому их нельзя просто отправить. В таких случаях браузеры отправляют предварительные запросы (preflight request). Сначала - запрос OPTIONS. Он также не должен ничего изменить на сервере. Если ответ содержит заголовок Access-Control-Allow-Origin с соответствующим значением, и ответ положительный, на сервер будет отправлен ещё один запрос, чтобы получить ресурсы.
Поскольку REST API не имеют состояния, сервер должен проверять все запросы. Но мы можем указать (через заголовок Access-Control-Max-Age), что предварительный запрос не обязательно делать каждый раз. В .NET это можно легко настроить внутри метода AddCors:
builder.Services.AddCors(opts =>
{
opts.AddPolicy("CORS API Endpoint",
bldr =>
{
bldr.
…
.SetPreflightMaxAge(TimeSpan.FromMinutes(10));
});
});
SetPreflightMaxAge даст указание клиенту кэшировать предварительный запрос CORS на 10 минут (т.е. он отправляет заголовок Access-Control-Max-Age со значением 600).
Источники:
- Кристиан Венц “Безопасность ASP.NET Core”. М.: ДМК Пресс, 2023. Глава 4.
- https://steven-giesel.com/blogPost/5fc7cb62-d5ad-4f07-831c-d6c1c6974641/cache-cors-preflight-requests