Sisk

Включение CORS (Cross-Origin Resource Sharing) в Sisk

Эта страница переведена с английского автоматически. Читать оригинал

Sisk предоставляет инструмент, который может быть полезен для обработки Cross-Origin Resource Sharing (CORS) при публичном предоставлении вашего сервиса. Эта возможность не является частью протокола HTTP, а представляет собой специфическую функцию веб‑браузеров, определённую W3C. Этот механизм безопасности препятствует веб‑странице выполнять запросы к другому домену, отличному от того, с которого была получена сама страница. Поставщик сервиса может разрешить доступ к своим ресурсам определённым доменам или только одному.

Один и тот же источник #

Чтобы ресурс был идентифицирован как «один и тот же источник», запрос должен содержать заголовок Origin:

HTTP
GET /api/users HTTP/1.1
Host: example.com
Origin: http://example.com
...

А удалённый сервер должен ответить заголовком Access-Control-Allow-Origin со значением, совпадающим с запрошенным origin:

HTTP
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
...

Эта проверка явная: хост, порт и протокол должны совпадать с запрошенными. Рассмотрим пример:

  • Сервер отвечает, что его Access-Control-Allow-Origin равен https://example.com:
    • https://example.net – домен отличается.
    • http://example.com – схема отличается.
    • http://example.com:5555 – порт отличается.
    • https://www.example.com – хост отличается.

В спецификации допускается только синтаксис для обоих заголовков, как для запросов, так и для ответов. Путь URL игнорируется. Порт также опускается, если он является портом по умолчанию (80 для HTTP и 443 для HTTPS).

HTTP
Origin: null
Origin: <scheme>://<hostname>
Origin: <scheme>://<hostname>:<port>

Включение CORS #

Нативно у вас есть объект CrossOriginResourceSharingHeaders внутри вашего ListeningHost.

Вы можете настроить CORS при инициализации сервера:

C#
static async Task Main(string[] args)
{
    using var app = HttpServer.CreateBuilder()
        .UseCors(new CrossOriginResourceSharingHeaders(
            allowOrigin: "http://example.com",
            allowHeaders: ["Authorization"],
            exposeHeaders: ["Content-Type"]))
        .Build();

    await app.StartAsync();
}

Код выше отправит следующие заголовки для всех ответов:

HTTP
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Headers: Authorization
Access-Control-Expose-Headers: Content-Type

Эти заголовки должны отправляться во всех ответах клиенту, включая ошибки и перенаправления.

Вы можете заметить, что класс CrossOriginResourceSharingHeaders имеет два похожих свойства: AllowOrigin и AllowOrigins. Обратите внимание, что одно — во множественном числе, другое — в единственном.

  • Свойство AllowOrigin статично: только указанный вами origin будет отправлен во всех ответах.
  • Свойство AllowOrigins динамично: сервер проверяет, содержится ли origin запроса в этом списке. Если найдено, он будет отправлен в ответе для данного origin.

Когда разрешённый origin зависит от запроса, то есть когда вы используете AllowOrigins или значение AutoAllowOrigin, описанное ниже, Sisk также добавляет заголовок Vary: Origin, чтобы кэши не переиспользовали ответ для другого origin.

Подстановочные знаки и автоматические заголовки #

В качестве альтернативы можно использовать подстановочный символ (*) в origin ответа, указывая, что любой origin разрешён для доступа к ресурсу. Однако такое значение недопустимо для запросов, содержащих учётные данные (заголовки авторизации), и такая операция приведёт к ошибке.

Обойти эту проблему можно, явно перечислив разрешённые origin через свойство AllowOrigins или используя константу AutoAllowOrigin в значении AllowOrigin. Это «магическое» свойство задаст заголовок Access-Control-Allow-Origin со значением, совпадающим с заголовком Origin запроса.

Также можно использовать AutoFromRequestMethod и AutoFromRequestHeaders для поведения, аналогичного AllowOrigin, которое автоматически отвечает на основе полученных заголовков.

C#
using var host = HttpServer.CreateBuilder()
    .UseCors(new CrossOriginResourceSharingHeaders(
        
        // Отвечает в зависимости от заголовка Origin запроса
        allowOrigin: CrossOriginResourceSharingHeaders.AutoAllowOrigin,
        
        // Отвечает в зависимости от заголовка Access-Control-Request-Method или метода запроса
        allowMethods: [CrossOriginResourceSharingHeaders.AutoFromRequestMethod],

        // Отвечает в зависимости от заголовка Access-Control-Request-Headers или отправленных заголовков
        allowHeaders: [CrossOriginResourceSharingHeaders.AutoFromRequestHeaders],

        exposeHeaders: [HttpKnownHeaderNames.ContentType, "X-Authenticated-Account-Id"],
        allowCredentials: true))
    .Build();

Другие способы применения CORS #

Если вы работаете с service providers, вы можете переопределить значения, определённые в файле конфигурации:

C#
static async Task Main(string[] args)
{
    using var app = HttpServer.CreateBuilder()
        .UsePortableConfiguration(...)
        .UseCors(cors => {
            // Перезапишет origin, определённый в конфигурации
            // файл.
            cors.AllowOrigin = "http://example.com";
        })
        .Build();

    await app.StartAsync();
}

Отключение CORS на конкретных маршрутах #

Свойство UseCors доступно как для маршрутов, так и для всех атрибутов маршрутов и может быть отключено следующим примером:

C#
[RoutePrefix("api/widgets")]
public class WidgetController : Controller {

    // GET /api/widgets/colors
    [RouteGet("/colors", UseCors = false)]
    public IEnumerable<string> GetWidgets() {
        return new[] { "Green widget", "Red widget" };
    }
}

Замена значений в ответе #

Вы можете явно заменить или удалить значения в действии роутера:

C#
[RoutePrefix("api/widgets")]
public class WidgetController : Controller {

    public IEnumerable<string> GetWidgets(HttpRequest request) {

        // Удаляет заголовок Access-Control-Allow-Credentials
        request.Context.OverrideHeaders.AccessControlAllowCredentials = string.Empty;
        
        // Заменяет заголовок Access-Control-Allow-Origin
        request.Context.OverrideHeaders.AccessControlAllowOrigin = "https://contorso.com";

        return new[] { "Green widget", "Red widget" };
    }
}

Preflight‑запросы #

Preflight‑запрос — это запрос метода OPTIONS, который клиент отправляет перед фактическим запросом.

Когда запрос с методом OPTIONS совпадает с путём маршрута, но для OPTIONS явно не настроен маршрут, сервер Sisk отвечает 200 OK и соответствующими CORS‑заголовками, после чего клиент может выполнить фактический запрос.

  • Если маршрут явно сопоставлен с RouteMethod.Options, например с помощью MapOptions, этот маршрут обрабатывает запрос вместо автоматического ответа.
  • Если у маршрута, совпадающего с путём, установлено UseCors = false, автоматический ответ всё равно отправляется, но без CORS‑заголовков.

Примечание

До версии Sisk 1.7 автоматический ответ на preflight‑запрос всегда включал CORS‑заголовки, даже для маршрутов с UseCors = false.

Отключение CORS глобально #

Это невозможно. Чтобы не использовать CORS, просто не настраивайте его.

Sisk распространяется с открытым исходным кодом по лицензии MIT.

Начните вводить, чтобы искать по документации и справочнику API.