Когда хотим знать IP клиента…
В процессе разработки web приложения на 1С, и это не шутка))), а пожелание заказчика, возникла ситуации когда понадобилось знать, с какого IP подключался клиент.
- Описание
- Подробнее
Описание
Не судите строго, пишу первый раз. Хотя и были попытки до этого, но так не разу не опубликовал.
И так есть самописная конфигурация на Платформе 8.3 с кусками БСП, в ней реализован некий web сервис для клиентов компании.
В web сервисе есть графический интерфейс для работы из браузера и rest api.
Была поставлена задача определять IP адрес клиента в независимости от того как он подключился к сервису.
Помогли в решении эти посты:
//sale.itcity.ru/public/1159393/
//sale.itcity.ru/public/1157609/
//sale.itcity.ru/public/338126/
И так у нас два варианта подключения. Начнем пожалуй с самого простого и быстрого получить IP при подключении через REST API.
Клиент получает данные через HTTP запросы, обращается к 1С базе через web сервер Apache.
Apache знает IP клиента, и мы можем поместить это адрес в некую переменную.
Для этого нам нужно:
в файле конфигурации Apache раскомментировать строки
#LoadModule headers_module modules/mod_headers.so
#LoadModule ssl_module modules/mod_ssl.so
добавить строки
# Проброс ip адреса клиента для http-сервисов 1С в заголовке client_ip_addr
RequestHeader append client_ip_addr "%{REMOTE_ADDR}s"
во все вызовы HTTP методов в базе 1С внедрить код, получающий ip клиента от Apache. В нашем случаи мы делаем так. Главное это как видите получения IP изи Заголовка http запроса.
Попытка
ipКлиентаОпределен = ПараметрыСеанса.IpКлиента;
Исключение
ПараметрыСеанса.IpКлиента = Запрос.Заголовки.Получить("client_ip_addr");
КонецПопытки;
И мы сохраняем в параметры сеанса, так как пока сеанс жив то нет смысла обновлять IP.
С эти всё оказалось просто. Но есть второй случай когда клиент не использует REST API, а заходит через web клиент.
Как узнать внешний IP клиента?
Мы решили так, пусть клиент сам нам скажет свой IP.
Но просить его писать это в поле ещё и смотреть в интернете было бы странно.
Значить это должно сделать за него 1С.
Решение приведенное ниже не является красивым, и работает не во всех 100% случаев (об этом ниже).
Клиент 1С сам по себе не имеет информации о настройках сетевой карты машины, на которой он исполняется, и даже если получит эту информацию — она может ничего не сказать о внешнем ip-адресе.
Очевидным решением является "спросить" у общедоступных сервисов наш ip-адрес с использованием HTTP-подключения например к ресурсу https://json.geoiplookup.io/api
.
Но это невозможно сделать для web-клиента, т.к. методы работы с HTTP недоступны на web-клиенте
Значит нужно какое-то другое решение, и таким решением будет отображение поля HTML документа с js, а из js можно вызвать get https://json.geoiplookup.io/api
.
Нюанс: просто создать HTML-документ недостаточно: js внутри него не отработает, также просто создать и не отображать форму не выйдет — js в поле HTTP-документа запускается только при отображении, в этом и состоит "некрасивость" решения — пользователю придется лицезреть запуск этой формы.
Если сделать пару допущений, то станет ясно, что все не так плохо:
Допущение №1 — скорость выполнения обработки достаточно высокая, чтобы пользователь ее не заметил В толстом и тонком клиенте выполнение такой обработки будет почти мгновенным и пользователем (вероятно) останется незамеченным. Что же до web-клиента? там все медленно!
Допущение №2 — такую обработку имеет смысл запускать при старте приложения и в этот момент можно ее чем-нибудь прикрыть Можно запустить свое лого на web-страничке во время загрузки web-клиента и отображения окна обработки.
- Первое что понадобится — место где мы будем хранить полученную информацию, в нашем случае это параметр сеанса
IpКлиента
. - Второе — сама обработка
Создаем обработку, и форму в ней (на самом деле подойдет любой вид форм).
На форму добавляем строковый реквизит "HTMLДокумент", в элементах задаем ему видПоле HTML документа
и целочисленный реквизит "ПопытокПолученияIPСделано", он будет хранить информацию о том как долго длится попытка получения ip-адреса.
Код модуля формы:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Попытка
// синхронный запрос GET
HTMLДокумент = "
|<!DOCTYPE html>
|<html>
| <head>
| <meta charset='UTF-8'>
| <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js' type='text/javascript'></script>
| </head>
| <body></body>
| <script>
| function httpGet(theUrl) {
| var xmlHttp = new XMLHttpRequest();
| xmlHttp.open( 'GET', theUrl, false ); // false for synchronous request
| xmlHttp.send( null );
| return xmlHttp.responseText;
| }
|
| getIp = function() {
| try {
| responseText = httpGet('https://api.ipify.org/?format=json');
| JSONresponce = JSON.parse(responseText);
| $('body').text(JSONresponce.ip);
| } catch(ex) {
| try {
| responseText = httpGet('https://ipinfo.io/json');
| JSONresponce = JSON.parse(responseText);
| $('body').text(JSONresponce.ip);
| } catch(ex) {
| try {
| responseText = httpGet('https://www.cloudflare.com/cdn-cgi/trace?format=json');
| JSONresponce = JSON.parse(responseText);
| $('body').text(JSONresponce.ip);
| } catch(ex) {
| $('body').text('Блокирован');
| }
| }
| }
| };
|
| $(document).ready(function() {
| getIp();
| });
| </script>
|</html>";
Исключение
КонецПопытки;
// синхронное получение получает пустой ip т.к. js исполняется в отдельном потоке
// асинхронное получение/ожидание
ПодключитьОбработчикОжидания("ПопытатьсяПолучитьСодержаниеHTML", 1, Ложь);
КонецПроцедуры
&НаКлиенте
Процедура ПопытатьсяПолучитьСодержаниеHTML()
Страница = Элементы.HTMLДокумент.Документ;
Попытка
ПопытокПолученияIPСделано = ПопытокПолученияIPСделано + 1;
Если ЗначениеЗаполнено(Страница.body.innerHTML) Тогда
ip = Страница.body.innerHTML;
ОтключитьОбработчикОжидания("ПопытатьсяПолучитьСодержаниеHTML");
СохранитьКлиентскийIPВПараметрыСеанса(ip);
ЭтаФорма.Закрыть();
КонецЕсли;
Исключение
КонецПопытки;
// если попытки получения неудачны - закончить
Если ПопытокПолученияIPСделано > 3 Тогда
ОтключитьОбработчикОжидания("ПопытатьсяПолучитьСодержаниеHTML");
ЭтаФорма.Закрыть();
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура СохранитьКлиентскийIPВПараметрыСеанса(ip)
ПараметрыСеанса.IpКлиента = ip;
КонецПроцедуры
ПриОткрытии() устанавливается тело HTML-документа с js, в js происходит попытка получения ip-адреса из одного из трех сервисов последовательно.
Если в браузере используется блокировщик — адреса ресурсов могут быть недоступны, в связи с чем получение ip-адреса ни из одного из ресурсов может не произойти, необходимо будет искать сервис, не находящийся в базе блокировщиков.
При успехе исполнения ip-адрес записывается в тело документа.
Каждую секунду в течение 3 секунд происходит асинхронная попытка получения текста HTML-документа (процедура ПопытатьсяПолучитьСодержаниеHTML()), в случае успеха происходит запись полученного значения в параметр сеанса.
После успеха или неуспеха форма закрывается.
- Третье — время ее запуска. В нашем случае это модуль приложения, ПриНачалеРаботыСистемы().
При начале работы системы запускаем обработку, активируем начальную страницу в попытке скрыть процесс работы обработки
Процедура ПриНачалеРаботыСистемы()
// СтандартныеПодсистемы
СтандартныеПодсистемыКлиент.ПриНачалеРаботыСистемы();
// Конец СтандартныеПодсистемы
// определение ip-адреса клиента
ОткрытьФорму("Обработка.УстановкаIpКлиентаВПараметрыСеанса.Форма.Форма");
// активизировать главное окно
ОкнаПриложения = ПолучитьОкна();
Для Каждого Окно Из ОкнаПриложения Цикл
Если Окно.НачальнаяСтраница = Истина Тогда
Окно.Активизировать();
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Вот и всё. Теперь мы почти всегда знаем, с какого IP к нам подключался клиент.
Надеюсь, в статье всё понятно. Ждём комментариев.