
Кнопка «Click to Call» на сайте — это «инновация», которой уже около 10 лет. Технологии под капотом изменились, а принцип остался прежним — кликаем по кнопке на странице сайта, запускается JavaScript, который запрашивает доступ к микрофону и устанавливает соединение с сервером — WebRTC SIP шлюзом. Далее одна клиент-серверная нога — это браузер-шлюз, вторая нога может быть сколь угодно длинной и через цепочку SIP proxy может соединяться в конечном счете с мобильным или стационарным телефоном. Таким образом, браузер превращается, в каком-то смысле, в софтфон и становится полноправным участником VoIP телефонии.

Звонок в один клик очень удобен для пользователей, которые заходят на сайт через мобильные браузеры, а таких, на сегодняшний день, большинство. Кроме удобства для клиентов, еще есть возможность сэкономить. Можно настроить так, что звонок с кнопки «Click to Call» будет тарифицироваться, как звонок внутри вашей АТС, т.е. в большинстве случаев бесплатно. Экономия, по сравнению с подключением и ежемесячным обслуживанием номера 8-800, достаточно большая.
У нас на сайте есть минимальный пример внедрения такой кнопки. В примере все хорошо — код простой, бери, копируй и радуйся. Но есть одно НО! Передавать параметры подключения к SIP серверу в JS коде небезопасно, злоумышленник легко может подменить номер вызываемого абонента или использовать учетные данные вашего SIP сервера, чтобы звонить пингвинам в Антарктиду и пересказывать им «Войну и Мир». И тогда прибыль от размещения на сайте кнопки «Click to call» может превратиться в огромные убытки.
Поэтому для Production реализации мы рекомендуем хранить параметры подключения на стороне сервера и подставлять их при инициализации звонка. Для этого можно использовать REST Hook скрипты.
Крючки? Какие такие крючки?
REST Hook — это простые скрипты, которые работают с JSON в теле HTTP-запроса и отдают JSON в теле HTTP ответов. Скрипты REST Hook подменяют собой стандартные приложения WCS API и позволяют обрабатывать данные о коннектах, звонках и видеопотоках на бэкенд сервере.
REST Hook могут быть использованы для следующих целей:
-
Аутентификация коннектов к серверу по токену или по паролю
-
Получение в реальном времени информации о коннектах, дисконнектах, начале и завершении потоков, звонков, и т.д.
-
Переопределение данных, переданных с клиента. Например, можно переопределить и скрыть реальное имя потока или направление звонка.
-
Реализация кастомного сигналинга с передачей данных через WebSockets, например рассылка текстового сообщения в чате всем подключенным клиентам.
В этой статье рассмотрим, как можно безопасно передать учетные данные SIP сервера и номер вызываемого абонента для кнопки «Click to Call» с использованием технологии REST Hook.
Что нужно для работы?
-
Фронтенд Web сервер — организует интерфейс с пользователем. Отображает web страницу с кнопкой «Click to Call».
-
WCS — посредник между пользователем и SIP сервером. Конвертирует WebRTC поток от браузера в SIP формат.
-
Бэкенд сервер — Web сервер, который обеспечивает работу REST Hook.
-
SIP сервер и SIP телефон.
Логически мы разделяем фронтенд, бэкенд и WCS серверы, но физически их можно разместить на одной машине. В этой статье, для упрощения разбора примера, мы используем три отдельные виртуальные машины.

Что делать и как настраивать?
Начнем настройку с бэкенд сервера.
Устанавливаем и конфигурируем Nginx и PHP, например Nginx на CentOS 7.
После чего, в файле /etc/nginx/nginx.conf в секции «server» добавляем следующие строки. Эта настройка обеспечит доступность нашего REST Hook скрипта для событий «/connect» и «/call»:
location / { try_files $uri $uri/ /index.php?$request_uri; }

В каталоге для файлов web сервера (у нас /var/www ) создаем файл «index.php», в нем размещаем основной код создаваемого REST Hook. Этот скрипт будет реализовывать проверку доступа по домену и передавать параметры подключения к SIP серверу и номер вызываемого абонента:
<?php $api_method = array_pop(explode("/", $_SERVER['REQUEST_URI'])); $incoming_data = json_decode(file_get_contents('php://input'), true); $domain = "yourdomain.com"; switch($api_method) { case"connect": $origin = $incoming_data['origin']; //logs error_log("sessionId: " . $incoming_data['sessionId']); error_log("origin: " . $origin); $found = strpos($origin, $domain); if ($found !== false){ error_log("User authorized by domain " . $domain); }else{ error_log("User not authorized by domain: " . $domain . " Connection failed with 403 status."); ubnormalResponse(403); } $rest_client_config = json_decode(file_get_contents('rest_client_config.json'), true); $incoming_data['restClientConfig'] = $rest_client_config; $incoming_data['sipLogin'] = "10001"; $incoming_data['sipAuthenticationName'] = "10001"; $incoming_data['sipPassword'] = "Password_123"; $incoming_data['sipDomain'] = "172.16.30.156"; $incoming_data['sipOutboundProxy'] = "172.16.30.156"; $incoming_data['sipPort'] = "5060"; break; case "call": // Callee Number $incoming_data['callee'] = "10002"; break; } header('Content-Type: application/json'); echo json_encode($incoming_data); function ubnormalResponse($code) { if ($code == 403) { header('HTTP/1.1 403 Forbidden', true, $code); } else { header(':', true, $code); } die(); } ?>

В том же каталоге /var/www создаем файл «rest_client_config.json». Исходный код можно найти в конце этой страницы.
В файле «rest_client_config.json» правим секцию «call» . Здесь указываем политику REST метода — перезаписывать данные и какое значение будет перезаписано с помощью скрипта:
"call" : { "clientExclude" : "", "restExclude" : "", "restOnError" : "LOG", "restPolicy" : "OVERWRITE", "restOverwrite" : "callee" },

Затем, переходим к WCS серверу. Предполагаем, что у вас уже есть установленный и настроенный экземпляр WCS. Если нет, то устанавливаем по этой инструкции. WCS можно запустить как виртуальный инстанс на Amazon, Google Cloud и DigitalOcean, или как контейнер в Docker.
В консоли вашего сервера с WCS проверяем доступность скрипта REST Hook для событий /connect и /call с помощью утилиты «Curl»:
curl http://172.16.30.123/connect curl http://172.16.30.123/call
замените 172.16.30.123 на IP адрес или доменное имя вашего бэкенд web сервера.

Если вывод утилиты Curl содержит нужную информацию — параметры для SIP подключения и номер вызываемого абонента — значит REST Hook мы настроили правильно.
Заходим в CLI WCS сервера:
ssh -p 2001 admin@localhost
в этой команде ничего менять не нужно, пароль по умолчанию: admin
и меняем приложение по умолчанию для обработки событий «/connect» и «/call» на наш новый REST Hook скрипт с помощью команды:
update app -l http://172.16.30.123/ defaultApp
замените 172.16.30.123 на IP адрес или доменное имя вашего бэкенд web сервера.

После всех этих настроек переходим к фронтенд web серверу.
Больше кода богу кода!
Создаем на фронтенде два пустых файла Click-to-Call-min.html и Click-to-Call-min.js. Эти файлы будут содержать минимальный код для реализации кнопки «Click to call».
HTML код:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <script type="text/javascript" src="https://flashphoner.com/downloads/builds/flashphoner_client/wcs_api-2.0/current/flashphoner.js"></script> <script type="text/javascript" src="Click-to-Call-min.js"></script> </head> <body onload="init_page()"> <input type="button" id="callBtn" type="button" Value="Call"/> <div id="remoteAudio"></div> <div id="localAudio"></div> </body> </html>
Из JS кода минимального примера убираем данные для подключения к SIP серверу и номер вызываемого абонента.
JS код:
var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var CALL_STATUS = Flashphoner.constants.CALL_STATUS; var localAudio; var remoteAudio; function init_page(){ Flashphoner.init({}); localAudio = document.getElementById("localAudio"); remoteAudio = document.getElementById("remoteAudio"); connect(); } function connect() { var url = "wss://172.16.30.124:8443" var sipOptions = { registerRequired: true }; var connectionOptions = { urlServer: url, sipOptions: sipOptions }; console.log("Create new session with url " + url); Flashphoner.createSession(connectionOptions).on(SESSION_STATUS.ESTABLISHED, function (session) { console.log(SESSION_STATUS.ESTABLISHED); }).on(SESSION_STATUS.REGISTERED, function (session) { console.log(SESSION_STATUS.REGISTERED); }); callBtn.onclick = call } function call(session) { var constraints = { audio: true, video: false }; var session = Flashphoner.getSessions()[0]; var outCall = session.createCall({ remoteVideoDisplay: remoteAudio, localVideoDisplay: localAudio, constraints: constraints, stripCodecs: "SILK" }); outCall.call(); callBtn.value = "Hangup"; callBtn.onclick = function () { callBtn.value = "Call"; outCall.hangup(); connect(); } }
Алло, девушка, соедините
Для тестирования нам понадобятся:
-
Тестовый стенд, который мы собрали выше (фронтeнд, WCS, бэкенд);
-
SIP сервер
-
Два SIP аккаунта
-
Браузер
-
Программный SIP телефон
Данные для подключения SIP аккаунта 10001 мы передаем в JS коде страницы с кнопкой «Click to Call» при помощи REST Hook. Кнопка «Click to Call» запрограммирована сделать вызов на номер 10002. Учетные данные для аккаунта 10002 внесем в программный SIP телефон.
Открываем созданную на фронтенд web сервере HTML страницу и нажимаем кнопку «Call»:

Принимаем входящий звонок на программном SIP телефоне и проверяем, что происходит обмен аудио потоками между абонентами:

Пришлось немного поработать, но результат того стоит. Теперь учетные данные SIP сервера и номер вызываемого абонента защищены от злоумышленников, и можно не переживать, что кто-то использует вашу телефонию в своих интересах.
Ссылки
Онлайн звонок с сайта по кнопке
Файл настроек flashphoner.properties
Документация по быстрому развертыванию Web Call Server
ссылка на оригинал статьи https://habr.com/ru/company/flashphoner/blog/566360/
Добавить комментарий