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

Библиотека Leaflet сама по себе не реализует механизмы аутентификации пользователей или авторизации доступа к данным. Однако в реальных веб-приложениях карта часто взаимодействует с защищёнными сервисами:

  • REST API;
  • геосервисами;
  • корпоративными картографическими платформами;
  • приватными тайловыми серверами;
  • сервисами геокодирования;
  • системами мониторинга и трекинга.

Во всех подобных случаях запросы к серверу должны содержать данные для подтверждения личности пользователя или приложения.

Наиболее распространённые способы аутентификации:

  • API-ключи;
  • Bearer Token (JWT);
  • OAuth 2.0;
  • Cookie-based Authentication;
  • пользовательские HTTP-заголовки;
  • подписанные URL.

Leaflet выступает клиентом, который отправляет запросы к защищённым ресурсам и отображает полученные данные на карте.


Аутентификация через API-ключ

Многие картографические сервисы используют API-ключ как самый простой механизм контроля доступа.

Пример URL с ключом:

https://api.example.com/tiles/{z}/{x}/{y}.png?apikey=YOUR_KEY

Подключение слоя:

const map = L.map('map').setView([55.751244, 37.618423], 10);

L.tileLayer(
    'https://api.example.com/tiles/{z}/{x}/{y}.png?apikey=YOUR_KEY',
    {
        maxZoom: 19
    }
).addTo(map);

При каждом запросе тайла ключ автоматически передаётся серверу.

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

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

Недостатки:

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

Хранение API-ключей

Никогда не следует считать клиентский код безопасным местом хранения секретов.

Плохой вариант:

const apiKey = "super-secret-key";

После публикации приложения значение становится доступным любому пользователю через инструменты разработчика.

Более безопасный подход:

fetch('/api/config')
    .then(response => response.json())
    .then(config => {
        L.tileLayer(
            `https://api.example.com/{z}/{x}/{y}.png?apikey=${config.key}`
        ).addTo(map);
    });

Даже в этом случае ключ остаётся доступным клиенту, поэтому рекомендуется:

  • ограничивать домены использования;
  • задавать квоты;
  • использовать временные токены;
  • скрывать критически важные операции за серверным прокси.

Использование Bearer Token

Современные API часто используют JWT-токены.

Типичный HTTP-заголовок:

Authorization: Bearer eyJhbGciOi...

Стандартный слой L.TileLayer не позволяет напрямую задавать произвольные заголовки для загрузки тайлов через элемент <img>.

Поэтому для защищённых ресурсов обычно применяется один из следующих подходов:

  1. Передача токена в URL.
  2. Использование собственного класса слоя.
  3. Проксирование запросов через сервер.

Получение данных через Fetch API

Часто геоданные загружаются отдельно, а затем отображаются на карте.

Пример:

fetch('/api/points', {
    headers: {
        Authorization: `Bearer ${token}`
    }
})
.then(response => response.json())
.then(data => {

    L.geoJSON(data).addTo(map);

});

Сервер получает токен и может определить права пользователя.

Такой подход особенно распространён при работе с:

  • GeoJSON;
  • маршрутами;
  • объектами мониторинга;
  • статистическими данными;
  • пространственной аналитикой.

Загрузка GeoJSON с аутентификацией

Рассмотрим полный пример.

async function loadObjects() {

    const response = await fetch('/api/objects', {
        headers: {
            Authorization: `Bearer ${token}`
        }
    });

    const geojson = await response.json();

    L.geoJSON(geojson, {
        onEachFeature(feature, layer) {
            layer.bindPopup(feature.properties.name);
        }
    }).addTo(map);
}

loadObjects();

Процесс выглядит следующим образом:

  1. Пользователь проходит авторизацию.
  2. Сервер выдаёт JWT.
  3. JWT сохраняется на клиенте.
  4. Leaflet получает данные через Fetch API.
  5. Сервер проверяет токен.
  6. Возвращаются разрешённые объекты.

Аутентификация через Cookies

Во многих корпоративных приложениях применяется сессионная авторизация.

После входа пользователь получает cookie:

Set-Cookie: sessionid=123456

При выполнении запросов браузер автоматически отправляет cookie серверу.

Пример:

fetch('/api/objects', {
    credentials: 'include'
})
.then(response => response.json())
.then(data => {
    L.geoJSON(data).addTo(map);
});

Параметр:

credentials: 'include'

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


Работа с защищёнными WMS-сервисами

Leaflet активно используется для подключения WMS-серверов.

Пример слоя:

L.tileLayer.wms(
    'https://maps.example.com/wms',
    {
        layers: 'roads',
        format: 'image/png',
        transparent: true
    }
).addTo(map);

Если сервер требует авторизацию через URL:

L.tileLayer.wms(
    'https://maps.example.com/wms?token=abc123',
    {
        layers: 'roads'
    }
).addTo(map);

Однако более безопасным считается использование серверного шлюза.

Схема работы:

Leaflet
    ↓
Backend Proxy
    ↓
Protected WMS

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


Проксирование запросов

Прокси-сервер часто используется для сокрытия токенов и ключей.

Архитектура:

Browser
    ↓
Application Server
    ↓
Map Service

Клиентский код:

L.tileLayer('/tiles/{z}/{x}/{y}')
    .addTo(map);

Сервер:

app.get('/tiles/:z/:x/:y', async (req, res) => {

    const url = createRemoteUrl(req.params);

    const response = await fetch(url, {
        headers: {
            Authorization: 'Bearer SECRET_TOKEN'
        }
    });

    response.body.pipe(res);
});

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

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

Создание собственного TileLayer с заголовками

Иногда возникает необходимость отправлять HTTP-заголовки для каждого тайла.

Для этого создаётся наследник L.TileLayer.

Пример концепции:

const SecureTileLayer = L.TileLayer.extend({

    createTile(coords, done) {

        const tile = document.createElement('img');

        const url = this.getTileUrl(coords);

        fetch(url, {
            headers: {
                Authorization: `Bearer ${token}`
            }
        })
        .then(response => response.blob())
        .then(blob => {

            tile.src = URL.createObjectURL(blob);

            done(null, tile);
        });

        return tile;
    }
});

Использование:

const layer = new SecureTileLayer(
    'https://api.example.com/{z}/{x}/{y}'
);

layer.addTo(map);

Такой механизм позволяет работать с сервисами, которые принимают данные аутентификации исключительно через заголовки.


Обновление JWT-токенов

JWT имеет ограниченный срок действия.

Типичная схема:

Access Token
    ↓
Истекает
    ↓
Refresh Token
    ↓
Получение нового Access Token

Перед загрузкой геоданных:

async function authorizedFetch(url) {

    let response = await fetch(url, {
        headers: {
            Authorization: `Bearer ${accessToken}`
        }
    });

    if (response.status === 401) {

        await refreshAccessToken();

        response = await fetch(url, {
            headers: {
                Authorization: `Bearer ${accessToken}`
            }
        });
    }

    return response;
}

После обновления токена карта продолжает работать без повторной авторизации пользователя.


Аутентификация при загрузке маркеров

Получение объектов карты часто зависит от прав доступа.

Пример:

async function loadMarkers() {

    const response = await fetch('/api/markers', {
        headers: {
            Authorization: `Bearer ${token}`
        }
    });

    const markers = await response.json();

    markers.forEach(marker => {

        L.marker([
            marker.lat,
            marker.lng
        ])
        .bindPopup(marker.title)
        .addTo(map);

    });
}

Сервер может возвращать:

  • все объекты;
  • объекты конкретного подразделения;
  • объекты определённого региона;
  • объекты, доступные конкретной роли.

Ролевая модель доступа

Аутентификация подтверждает личность пользователя.

Авторизация определяет его права.

Пример ролей:

Admin
Operator
Manager
Guest

После проверки токена сервер может фильтровать данные:

if (user.role === 'Guest') {
    return publicObjects;
}

if (user.role === 'Manager') {
    return regionalObjects;
}

return allObjects;

Leaflet получает уже отфильтрованный набор пространственных данных.


Защита WebSocket-соединений

Многие карты отображают данные в реальном времени.

Например:

  • транспорт;
  • GPS-трекинг;
  • телеметрия;
  • мониторинг датчиков.

Подключение:

const socket = new WebSocket(
    `wss://api.example.com/live?token=${token}`
);

После аутентификации сервер начинает передавать координаты:

socket.onmess age = event => {

    const data = JSON.parse(event.data);

    marker.setLatLng([
        data.lat,
        data.lng
    ]);

};

Ограничение доступа к тайлам

Некоторые организации публикуют карты исключительно для внутренних пользователей.

Популярные способы защиты:

  • VPN;
  • JWT;
  • API-ключи;
  • IP-фильтрация;
  • корпоративная авторизация SSO;
  • OAuth 2.0.

При использовании Leaflet схема не меняется: карта отображает только те тайлы, которые сервер разрешил получить после успешной проверки учётных данных.


OAuth 2.0 и Leaflet

OAuth применяется при интеграции с внешними сервисами.

Типичный процесс:

Пользователь
    ↓
OAuth Provider
    ↓
Access Token
    ↓
Leaflet Application
    ↓
Protected API

После получения токена карта может выполнять запросы:

fetch('/api/geo', {
    headers: {
        Authorization: `Bearer ${oauthToken}`
    }
})
.then(response => response.json())
.then(data => {
    L.geoJSON(data).addTo(map);
});

Такой подход широко используется при интеграции с облачными геоинформационными платформами и корпоративными системами единого входа.


Типичные ошибки при реализации аутентификации

Хранение токенов в исходном коде

Небезопасный вариант:

const token = "hardcoded-token";

Любой пользователь сможет получить значение.

Передача секретов через HTTP

Небезопасно:

http://example.com

Без шифрования токены могут быть перехвачены.

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

https://example.com

Слишком длительный срок жизни токенов

Плохо:

Access Token: 365 дней

Предпочтительнее:

Access Token: 5–30 минут
Refresh Token: длительный срок

Передача административных ключей клиенту

Недопустимо:

const adminKey = "...";

Подобные ключи должны использоваться только на серверной стороне.

Отсутствие проверки срока действия токена

Перед выполнением запросов необходимо контролировать актуальность токена и своевременно выполнять процедуру обновления.


Рекомендации по безопасной архитектуре

Для производственных систем наиболее распространён следующий подход:

Leaflet
    ↓
Backend API
    ↓
Authentication Service
    ↓
Protected GIS Services

Характерные особенности такой архитектуры:

  • все секреты находятся на сервере;
  • клиент использует короткоживущие токены;
  • запросы проходят через HTTPS;
  • реализована система обновления токенов;
  • ведётся журнал обращений;
  • выполняется проверка ролей и прав доступа;
  • защищённые картографические сервисы недоступны напрямую из браузера.

Такой подход обеспечивает безопасную работу Leaflet-приложений с корпоративными геоданными, приватными тайловыми серверами и внешними картографическими API.