- Предисловие
- Авторизация
- Чтение данных
- Дополнительно
Предисловие
Рассмотрим задачу по одностороннему обмену между 1С и MS SharePoint. Суть задачи - забирать данные из определенных таблиц в MS SharePoint и загрузить их в 1С. В данной статье будет продемонстрирована загрузка из таблицы AssignmentTimephasedDataSet.
В результате долгих поисков были найдены 3 способа прочитать данные из MS SharePoint. Для 2 из 3 найденных способов требовались определенные настройки на стороне MS SharePoint (Далее SP). Не буду углубляться в подробности, скажу лишь, что технических специалистов на стороне SP не было, чтобы выполнить настройку, пришлось работать с тем, что есть. Речь в статье пойдет про 3 способ, который позволяет получить информацию из любой таблицы с указанием конкретных отборов, через GET запросы.
Итак, чтобы прочитать информацию из SP, мы должны сначала авторизоваться. Здесь есть нюанс, у вас должны быть права на программную работу с SP. Например, я мог просматривать данные в SP через браузер, но при попытке программного вызова сваливалась ошибка с сообщением о несанкционированном доступе. После того как права мне расширили, начинается самое интересное - процесс авторизации. Пришлось штудировать Stack Overflow и десятки других сайтов и проецировать решения, предложенные там, сначала в Postman, а затем и в 1С. Алгоритм, представленный в сети, конкретно в моем случае не работал, и мне пришлось внести в него корректировки. Готовый алгоритм авторизации представлен ниже.
1. Итак, самое первое, что нам необходимо сделать - это авторизоваться в Майкрософт, выполнив POST запрос:
&НаСервере
Функция ТокенМайкрсофота()
ТексЗапроса = РеквизитФормыВЗначение("Объект").ПолучитьМакет("ЗапросДляТокена").ПолучитьТекст();
ТексЗапроса = СтрЗаменить(ТексЗапроса, "[Логин]", Логин);
ТексЗапроса = СтрЗаменить(ТексЗапроса, "[Пароль]", Пароль);
ТексЗапроса = СтрЗаменить(ТексЗапроса, "[Домен]", Домен);
ЗаголовкиHTTPЗапрос = Новый Соответствие;
ЗаголовкиHTTPЗапрос.Вставить("Accept", "application/json; odata=verbose");
HTTPЗапрос = Новый HTTPЗапрос("/extSTS.srf", ЗаголовкиHTTPЗапрос);
HTTPЗапрос.УстановитьТелоИзСтроки(ТексЗапроса, "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);
HTTPСоединение = Новый HTTPСоединение("login.microsoftonline.com",,,,,, Новый ЗащищенноеСоединениеOpenSSL);
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
ДанныеОтветаСтрокой = HTTPОтвет.ПолучитьТелоКакСтроку();
ТекстНачало ="xmlns:wsse=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">";
НачалоТекста = Найти(ДанныеОтветаСтрокой, ТекстНачало);
Если НачалоТекста = 0 Тогда
Возврат "";
КонецЕсли;
НачалоТекста = НачалоТекста + СтрДлина(ТекстНачало);
ТекстКонец = "</wsse:BinarySecurityToken>";
КонецТекста = Найти(ДанныеОтветаСтрокой, ТекстКонец);
Если КонецТекста = 0 Тогда
Возврат "";
КонецЕсли;
Возврат Сред(ДанныеОтветаСтрокой, НачалоТекста, КонецТекста - НачалоТекста);
КонецФункции
В результате мы получим токен доступа нашей учетной записи в Майкрософт.
2.Далее с этим токеном доступа мы стучимся на сайт SP.
&НаСервере
Функция КукиСервераSP()
ЗаголовкиHTTPЗапрос = Новый Соответствие();
ЗаголовкиHTTPЗапрос.Вставить("Content-Type", "text/plain");
ЗаголовкиHTTPЗапрос.Вставить("Cookie", BinarySecurityToken);
HTTPЗапрос = Новый HTTPЗапрос("/_forms/default.aspx?wa=wsignin1.0", ЗаголовкиHTTPЗапрос);
HTTPЗапрос.УстановитьТелоИзСтроки(BinarySecurityToken, "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);
HTTPСоединение = Новый HTTPСоединение(СерверSP(),,,,,, Новый ЗащищенноеСоединениеOpenSSL);
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
КукиЗаголовка = HTTPОтвет.Заголовки.Получить("Set-Cookie");
НачалоСтроки = ", SIMI=";
КонецСтроки = "domain=sharepoint.com; path=/; SameSite=None; secure, ";
Подстрока = ВыбратьПодСтроку(КукиЗаголовка, НачалоСтроки, КонецСтроки);
СтрокаДляУдаления = СтрШаблон("%1%2%3", НачалоСтроки, Подстрока, КонецСтроки);
Возврат СтрЗаменить(КукиЗаголовка, СтрокаДляУдаления, ";");
КонецФункции
В результате мы получаем Cookie от сервера SP, который нам потребуется для дальнейшей работы с таблицами.
3. Далее с этим куки мы стучимся еще по одному URL на сайте SP.
&НаСервере
Функция ТокенСервераSP()
Заголовки = Новый Соответствие();
Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
Заголовки.Вставить("Cookie", Cookie);
HTTpЗапрос = Новый HTTPЗапрос("/sites/pwa/_api/contextinfo", Заголовки);
Соединение = Новый HTTPСоединение(СерверSP(),,,,,, Новый ЗащищенноеСоединениеOpenSSL);
HTTPОтвет = Соединение.ОтправитьДляОбработки(HTTpЗапрос);
ДанныеОтветаСтрокой = HTTPОтвет.ПолучитьТелоКакСтроку();
ТекстНачало = "<d:FormDigestValue>";
НачалоТекста = Найти(ДанныеОтветаСтрокой, ТекстНачало);
Если НачалоТекста = 0 Тогда
Возврат "";
КонецЕсли;
НачалоТекста = НачалоТекста + СтрДлина(ТекстНачало);
ТекстКонец = "</d:FormDigestValue>";
КонецТекста = Найти(ДанныеОтветаСтрокой, ТекстКонец);
Если КонецТекста = 0 Тогда
Сообщить("В HTTPОтвет не удалось найти конец FormDigestValue");
Возврат "";
КонецЕсли;
Возврат Сред(ДанныеОтветаСтрокой, НачалоТекста, КонецТекста - НачалоТекста);
КонецФункции
Здесь мы получаем токен от сервера SP, который обрамлён в теги FormDigestValue
В итоге у нас имеются куки и токен (FormDigestValue) для программной авторизации на сайте SP. Эти данные живут некоторое время, если вдруг данных из таблиц перестали считываться, вероятнее всего, нужно обновить параметры авторизации.
Переходим к чтению данных. При считывании из таблиц существует ограничение на количество возвращаемых строк. Оно может быть любым в зависимости от настроек конкретно в вашей организации. В моем случае ограничение на таблице AssignmentTimephasedDataSet составило 2000 строк. В таблице Projects ограничение составило 500 строк.
Нам изначально требуется узнать общее количество строк в таблице. Это делается при помощи $count?. Например, количество записей за выбранный период составило 10000. Итого мы сможем за 5 обращений к таблице прочитать всю информацию.
На каждом витке цикла мы пропускаем указанное количество строк и начинаем загрузку со следующей строки. Пропустить ненужные строки можно при помощи $skip.
&НаСервере
Процедура ВыполнитьЧтениеДанных()
ТаблицаДанных.Очистить();
//Логика аналогичная запросу "С - ПО"
ОтборПоПериоду = СтрШаблон("&$filter=TimeByDay ge datetime'%1' and TimeByDay lt datetime'%2'",
Формат(Период.ДатаНачала, "ДФ=yyyy-MM-ddT00:00:00"), Формат(Период.ДатаОкончания, "ДФ=yyyy-MM-ddT23:59:59"));
//$count? - используется чтобы получить общее количество строк в таблице по указанному отбору
СервисКоличестваЗаписейВтаблице = СтрШаблон("/sites/pwa/_api/ProjectData/[en-us]/AssignmentTimephasedDataSet()/$count?%1", ОтборПоПериоду);
КоличествоЗаписейВтаблице = КоличествоСтрокВТаблице(СервисКоличестваЗаписейВтаблице);
КоличествоПрочитанныхСтрок = 0;
КоличествоИтераций = КоличествоЗаписейВтаблице/2000;
НомерСтроки = 1;
Заголовки = Новый Соответствие();
Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
Заголовки.Вставить("Cookie", Cookie);
Заголовки.Вставить("X-RequestDigest", FormDigestValue);
Соединение = Новый HTTPСоединение(СерверSP(),,,,,, Новый ЗащищенноеСоединениеOpenSSL);
ЧитаемыеКолонки = "AssignmentId,TimeByDay,TaskName";
Пока КоличествоИтераций > 0 Цикл
//$skip - используется чтобы пропустить указанное количество строк в таблице
Фильтр = ?(КоличествоПрочитанныхСтрок = 0, "", СтрШаблон("&$skip=%1", Формат(КоличествоПрочитанныхСтрок, "ЧГ=0")));
СервисПолученияДанных = СтрШаблон("/sites/pwa/_api/ProjectData/[en-us]/AssignmentTimephasedDataSet?$select=%1%2%3", ЧитаемыеКолонки, ОтборПоПериоду, Фильтр);
HTTpЗапрос = Новый HTTPЗапрос(СервисПолученияДанных, Заголовки);
HTTPОтветПозадачам = Соединение.Получить(HTTpЗапрос);
ПрочитатьОтветПоЗадачам(HTTPОтветПозадачам.ПолучитьТелоКакСтроку(), НомерСтроки);
КоличествоИтераций = КоличествоИтераций - 1;
КоличествоПрочитанныхСтрок = КоличествоПрочитанныхСтрок + 2000;
КонецЦикла;
КонецПроцедуры
При считывании мы получаем данные в XML формате. Парсим и загружаем в 1С
Дополнительно
Текст запроса из макета "ЗапросДляТокена":
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">https://login.microsoftonline.com/extSTS.srf</a:To>
<o:Security s:mustUnderstand="1"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:UsernameToken>
<o:Username>[Логин]</o:Username>
<o:Password>[Пароль]</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body>
<t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<a:EndpointReference>
<a:Address>https://[Домен].sharepoint.com/</a:Address>
</a:EndpointReference>
</wsp:AppliesTo>
<t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
</t:RequestSecurityToken>
</s:Body>
</s:Envelope>
P.s.
- Повторяющийся код по вызову http методов, созданию запросов и т.д. специально не стал делать универсальными для большей наглядности.