Аутентификация при запросах

Общая модель доступа к защищённым геосервисам

В веб-картографии аутентификация чаще всего требуется при работе с приватными слоями, корпоративными геосервисами и платными API, где доступ к тайлам, векторным данным или WMS/WMTS ресурсам ограничен. В OpenLayers запросы к данным выполняются через стандартные механизмы браузера (XHR или fetch), поэтому любые схемы аутентификации реализуются на уровне HTTP-запросов, конфигурации источников (source) или кастомных загрузчиков.

Ключевые ограничения, влияющие на архитектуру:

  • невозможность скрыть токены в клиентском коде полностью
  • зависимость от CORS при кросс-доменных запросах
  • необходимость учитывать кэширование тайлов браузером и CDN
  • различия между слоями растровых и векторных данных

Передача токенов через HTTP-заголовки

Наиболее универсальный способ аутентификации — добавление заголовков Authorization или кастомных header-параметров.

XYZ / Tile слои с заголовками

В стандартных ol/source/XYZ и ol/source/TileImage нет прямого параметра headers, но используется функция tileLoadFunction, позволяющая полностью контролировать загрузку:

import XYZ from 'ol/source/XYZ';
import TileLayer from 'ol/layer/Tile';

const source = new XYZ({
  url: 'https://api.example.com/tiles/{z}/{x}/{y}.png',
  tileLoadFunction: (tile, src) => {
    const img = tile.getImage();

    fetch(src, {
      headers: {
        Authorization: 'Bearer YOUR_TOKEN'
      }
    })
      .then(res => res.blob())
      .then(blob => {
        const objectUrl = URL.createObjectURL(blob);
        img.src = objectUrl;
      });
  }
});

const layer = new TileLayer({
  source
});

Особенности:

  • полный контроль над HTTP-запросом
  • возможность добавления любых заголовков
  • необходимость ручной обработки blob-ответа
  • отключение стандартного механизма загрузки изображений

Аутентификация через параметры URL

Самый простой, но менее безопасный подход — передача токена через query-параметры.

const source = new XYZ({
  url: 'https://api.example.com/tiles/{z}/{x}/{y}.png?token=YOUR_TOKEN'
});

Используется при:

  • публичных API с ограниченным доступом
  • временных signed URL
  • CDN-сервисах (Mapbox-style tile endpoints)

Риски:

  • токен попадает в историю браузера
  • логируется прокси-серверами
  • может кешироваться вместе с URL

Поддержка аутентификации в WMS и WMTS

WMS (Web Map Service)

Для WMS чаще всего применяется ol/source/ImageWMS или ol/source/TileWMS.

import ImageWMS from 'ol/source/ImageWMS';

const source = new ImageWMS({
  url: 'https://geo.example.com/geoserver/wms',
  params: {
    'LAYERS': 'workspace:layer',
    'TILED': true
  },
  ratio: 1,
  serverType: 'geoserver'
});

Добавление авторизации через headers:

source.setImageLoadFunction((image, src) => {
  const img = image.getImage();

  fetch(src, {
    headers: {
      Authorization: 'Basic ' + btoa('user:password')
    }
  })
    .then(r => r.blob())
    .then(blob => {
      img.src = URL.createObjectURL(blob);
    });
});

Особенности WMS:

  • часто используется Basic Auth или cookie-based сессии
  • важна поддержка withCredentials на уровне сервера
  • сервер должен корректно отвечать на preflight OPTIONS запросы

WMTS (TileMatrixSet)

WMTS чаще работает через шаблоны URL:

import WMTS from 'ol/source/WMTS';

const source = new WMTS({
  url: 'https://geo.example.com/wmts?token=YOUR_TOKEN',
  layer: 'layer_name',
  matrixSet: 'EPSG:3857'
});

При необходимости сложной авторизации используется кастомный tileLoadFunction, аналогично XYZ.


Если сервер использует сессии (например, после логина через форму), OpenLayers может автоматически отправлять cookies при включении credentials.

fetch('https://geo.example.com/session', {
  method: 'POST',
  credentials: 'include',
  body: JSON.stringify({
    login: 'user',
    password: 'pass'
  })
});

Далее все источники используют cookies:

const source = new XYZ({
  url: 'https://geo.example.com/tiles/{z}/{x}/{y}.png',
  tileLoadFunction: (tile, src) => {
    const img = tile.getImage();

    fetch(src, {
      credentials: 'include'
    })
      .then(r => r.blob())
      .then(blob => {
        img.src = URL.createObjectURL(blob);
      });
  }
});

Особенности:

  • требует корректной настройки CORS:

    • Access-Control-Allow-Credentials: true
    • конкретный origin, не *
  • подходит для корпоративных систем и внутренних порталов


Подпись запросов (Signed URLs)

Распространённый подход для публичных API — генерация временно действующих URL с подписью.

function buildSignedUrl(z, x, y) {
  const expires = Date.now() + 60 * 1000;

  const base = `/tiles/${z}/${x}/${y}.png`;
  const signature = md5(base + expires + SECRET_KEY);

  return `${base}?expires=${expires}&sig=${signature}`;
}

const source = new XYZ({
  url: (z, x, y) => buildSignedUrl(z, x, y)
});

Применяется при:

  • защите CDN
  • выдаче ограниченного доступа к тайлам
  • интеграции с платными картографическими сервисами

Преимущество — отсутствие необходимости хранить токен на клиенте в явном виде.


Interceptor на уровне fetch

Глобальная модификация запросов позволяет централизовать аутентификацию для всех слоёв.

const originalFetch = window.fetch;

window.fetch = function(url, options = {}) {
  const modifiedOptions = {
    ...options,
    headers: {
      ...(options.headers || {}),
      Authorization: 'Bearer YOUR_TOKEN'
    }
  };

  return originalFetch(url, modifiedOptions);
};

Используется в:

  • крупных приложениях с множеством источников
  • микрофронтендах
  • системах с динамическим обновлением токенов

Риски:

  • влияние на сторонние запросы
  • сложность отладки
  • конфликты с библиотеками

Аутентификация в VectorTile слоях

Vector tiles (ol/source/VectorTile) часто используют protobuf (MVT) и требуют более аккуратной работы с загрузкой.

import VectorTileSource from 'ol/source/VectorTile';
import MVT from 'ol/format/MVT';

const source = new VectorTileSource({
  format: new MVT(),
  url: 'https://api.example.com/tiles/{z}/{x}/{y}.pbf',
  tileLoadFunction: (tile, src) => {
    fetch(src, {
      headers: {
        Authorization: 'Bearer TOKEN'
      }
    })
      .then(r => r.arrayBuffer())
      .then(data => {
        tile.setFeatures(
          source.getFormat().readFeatures(data, {
            extent: tile.getExtent(),
            featureProjection: 'EPSG:3857'
          })
        );
      });
  }
});

Особенности:

  • используется arrayBuffer вместо изображения
  • требуется ручной парсинг формата
  • выше нагрузка на CPU по сравнению с растровыми тайлами

Работа с refresh-токенами

В долгоживущих сессиях токены устаревают, поэтому требуется механизм обновления.

let accessToken = await getAccessToken();

async function authFetch(url, options = {}) {
  let res = await fetch(url, {
    ...options,
    headers: {
      ...(options.headers || {}),
      Authorization: `Bearer ${accessToken}`
    }
  });

  if (res.status === 401) {
    accessToken = await refreshToken();
    res = await fetch(url, {
      ...options,
      headers: {
        ...(options.headers || {}),
        Authorization: `Bearer ${accessToken}`
      }
    });
  }

  return res;
}

Интеграция с OpenLayers:

tileLoadFunction: (tile, src) => {
  const img = tile.getImage();

  authFetch(src)
    .then(r => r.blob())
    .then(blob => {
      img.src = URL.createObjectURL(blob);
    });
}

CORS и серверные требования

Без корректной настройки CORS любые схемы аутентификации становятся неработоспособными.

Обязательные заголовки сервера:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Headers: Authorization
  • Access-Control-Allow-Methods: GET, OPTIONS

При cookie-аутентификации дополнительно:

  • Access-Control-Allow-Credentials: true

Особенности поведения браузера:

  • preflight-запросы для нестандартных headers
  • блокировка ответа при несовпадении origin
  • отсутствие доступа к response body при CORS error

Кэширование и безопасность токенов

OpenLayers активно использует кэширование тайлов, что влияет на безопасность:

  • URL с токеном может быть закеширован
  • одинаковые запросы повторно не выполняются
  • CDN может сохранять защищённые ресурсы

Для предотвращения утечек применяются:

  • короткоживущие токены
  • подпись URL с TTL
  • отключение cache-busting через уникальные query-параметры
  • серверное ограничение по IP или referrer

Архитектурные паттерны защиты данных

В сложных системах OpenLayers редко работает напрямую с секретами. Используются промежуточные слои:

  • backend-proxy для тайлов
  • gateway API с единым токеном
  • edge-функции (serverless прокси)
  • разделение публичных и приватных слоёв

Типичный прокси:

Client → OpenLayers → Backend Proxy → GIS Server

Преимущество:

  • токены не попадают в браузер
  • централизованный контроль доступа
  • возможность логирования и throttling

Недостаток:

  • дополнительная задержка
  • нагрузка на серверную часть

Итоговые модели интеграции

В OpenLayers аутентификация всегда реализуется через комбинацию уровней:

  • уровень запроса (fetch, tileLoadFunction)
  • уровень источника (source configuration)
  • уровень инфраструктуры (proxy, CDN, API gateway)
  • уровень браузера (cookies, credentials)

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