Проектирование структуры аутентификации для видеостриминга

Проектирование структуры аутентификации для видеостриминга

- Примеры реализации видеосервисов на основе ReactPlayer-

1. Обзор проекта

Данный случай представляет собой проект, осуществляемый для перехода от существующей системы пользовательских руководств на базе PDF к сервису на основе видеоконтента. В существующей системе пользователь нажимал кнопку руководства, и фронтенд напрямую получал доступ к AWS S3 для загрузки PDF-файла. Этот подход был прост в реализации, но имел ограничения: пользователям было трудно одновременно проверять экран работы и руководство, а также сложно было интуитивно передавать сложные процедуры.

Клиент требовал функции PIP (картинка в картинке), чтобы пользователи могли одновременно просматривать руководство в видеоролике, сохраняя открытым рабочий экран. В то же время необходимо было заблокировать прямой доступ к AWS S3 по политике безопасности, и существовали ограничения на использование чувствительной аутентификационной информации в URL-параметрах. Таким образом, ключевой аспект данного проекта заключался не только в простой реализации функции воспроизведения видео, но и в проектировании структуры аутентификации и потоковой передачи, которая удовлетворяет требованиям безопасности и основана на браузерном воспроизведении видео.

2. Выбор технологий и направление проектирования

В области Frontend была внедрена библиотека видеоплеера на основе React под названием ReactPlayer. ReactPlayer работает на основе тега HTML5 video, обладает высокой совместимостью с окружением React и имеет преимущества, такие как относительно простое управление состоянием воспроизведения и функция PIP.

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

Пример кода 1. Пример вызова ReactPlayer


url={videoEndpoint}
элементы управления
pip
playing={false}
ширина="100%"
высота="100%"
/>

Данный пример показывает структуру, при которой ReactPlayer воспроизводит видео на основе конечной точки (endpoint), полученной от сервера. Учетные данные не включены в URL, а обрабатываются путем передачи временного токена, выданного на этапе предварительной аутентификации, в Cookies.

3. Редизайн архитектуры

Существующая структура позволяла фронтенду напрямую обращаться к AWS S3. Однако при предоставлении видеоконтента поддержание такой структуры могло привести к раскрытию пути хранения, повторному использованию URL и возможному обходу контроля доступа. В связи с этим мы разделили роли сервера следующим образом.

Фронтенд
-> Сервер A (аутентификация пользователей и посредничество запросов на потоковое воспроизведение)
-> Сервер B (сервер только для просмотра файлов)
-> AWS S3

Сервер A отвечает за аутентификацию пользователей, проверку прав, выдачу временных токенов и промежуточную передачу запросов на потоковую передачу. Сервер B отделен для обработки только запросов на получение фактических видеофайлов и доступа к AWS S3. Это позволяет блокировать клиентам прямой доступ к пути хранения и настраивать так, чтобы все запросы на видео проходили проверку аутентификации сервера.

4. Процесс аутентификации и воспроизведения

Когда пользователь нажимает кнопку мануала, сначала не вызывается видеоплеер, а отправляется запрос API проверки пользователя на Сервер A. После завершения проверки Сервер A выдает конечную точку для воспроизведения видео и временный токен с ограниченным сроком действия. Временный токен сохраняется в Cookie браузера и передается вместе с вызовом конечной точки видео в ReactPlayer.

Пример кода 2. Запрос информации о воспроизведении видео

const openManualVideo = async (manualId: string) => {
const response = await api.get(`/api/videos/${manualId}/authorize`);
setVideoEndpoint(response.data.videoEndpoint);
setManualModalOpen(true);
};

Этот код является примером того, как отправить запрос на воспроизведение видеопотока, когда нажата кнопка «Руководство». На этом этапе сервер проверяет права пользователя и отвечает, сохраняя временный токен в Cookie.

Пример кода 3. Пример выдачи временного токена Cookie

ResponseCookie cookie = ResponseCookie.from("VIDEO_ACCESS_TOKEN", token)
.httpOnly(true)
.secure(true)
.path("/api/videos/")
.maxAge(Duration.ofMinutes(30))
.sameSite("Strict")
.build();

return ResponseEntity.ok()
.header(HttpHeaders.SET_COOKIE, cookie.toString())
.body(new VideoAuthorizeResponse(videoEndpoint));

В Cookie применены параметры HttpOnly, Secure и SameSite, чтобы ограничить доступ скриптов и ненужные внешние передачи. Кроме того, были указаны path и maxAge, чтобы они использовались только для запросов видео.

5. Обработка потокового вещания

Потоковая передача видео, в отличие от обычного скачивания файлов, непрерывно передает большие объемы данных, и при перемещении пользовательской позиции воспроизведения могут возникать частичные запросы на основе заголовка Range. Поэтому Сервер A должен передать заголовок Range клиента Серверу B, а также передать статус ответа и основные заголовки от Сервер B обратно клиенту.

Пример кода 4. Стриминг на основе заголовка Range

@GetMapping("/api/videos/{manualId}/stream")
публичный Mono >> поток(
@PathVariable String manualId,
@RequestHeader(value = HttpHeaders.RANGE, required = false) String range
) {
вернуться webClient.get()
.uri("/internal/files/{manualId}", manualId)
.headers(headers -> {
if (range != null) headers.set(HttpHeaders.RANGE, range);
})
.exchangeToMono(response -> {
Флюкс тело = response.bodyToFlux(DataBuffer.class);
ResponseEntity.BodyBuilder builder = ResponseEntity.status(response.statusCode());
response.headers().asHttpHeaders().forEach((name, values) -> {
если (isStreamingHeader(name)) builder.header(name, values.toArray(String[]::new));
});
return Mono.just(builder.body(body));
});
}

Ключевым моментом этого метода является то, что он не загружает весь файл в память, как в случае с bodyToMono(Resource.class) или bodyToMono(byte[].class). Используя bodyToFlux(DataBuffer.class), вы можете передавать видеоданные порциями (chunk), что подходит для обработки больших объемов видео.

Пример кода 5. Основная фильтрация заголовков потоковой передачи

private boolean isStreamingHeader(String name) {
return HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)
|| HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)
|| HttpHeaders.CONTENT_RANGE.equalsIgnoreCase(name)
|| HttpHeaders.ACCEPT_RANGES.equalsIgnoreCase(name)
|| HttpHeaders.CACHE_CONTROL.equalsIgnoreCase(name);
}

Заголовки, такие как Content-Range, Accept-Ranges и Content-Length, необходимы браузеру для определения длины видео и диапазона частичного ответа. Особенно важно сохранять эти заголовки для обеспечения функции навигации и стабильного воспроизведения.

6. Результаты и эффекты

Результаты реализации пользователи смогли сразу воспроизводить видео в браузере и одновременно использовать рабочий экран и видеоруководство через функцию PIP. Кроме того, было возможно удовлетворить требования безопасности, исключив прямой доступ к AWS S3 и применив серверный контроль доступа.

• Удаление прямого экспонирования AWS S3

• Удаление передачи конфиденциальной информации на основе URL

• Разрешение на доступ к видео endpoint после проверки пользователя

• Ограниченный контроль доступа на основе временных токенов

• Поддержка воспроизведения и навигации видео на основе заголовка диапазона

7. Заключение

В этом примере я не только добавил видеоплеер, но также получил опыт в проектировании с учетом работы ReactPlayer и тега HTML5 video, политики безопасности клиента, разделения ролей между серверами и способов обработки потоковой передачи. Разделив процесс аутентификации и воспроизведения и четко разделив ответственность между сервером A и сервером B, мы смогли одновременно обеспечить пользовательский опыт и безопасность.

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

NZ

Site footer