gifts2017

Кроссдоменные HTTP-запросы к 1С

Опубликовал Александр Анисков (vandalsvq) в раздел Программирование - Практика программирования

В чистом виде, так сказать, в естественной среде браузерного окружения, кроссдоменные запросы — штука не тривиальная. Но если сюда добавить 1С, то становится все совсем плохо.

Самое большое удобство в появлении http-сервисов, на мой взгляд, представляет возможность построения web-приложений, реализующих определенный функционал части конфигурации. Это могут быть какие угодно программы, от dashboard-ов до самостоятельных приложений. Практически любые ваши фантазии, подкрепленные возможностью организации rest архитектуры приложения.

В моем случае вопрос стал в разработке личного web-кабинета для системы управления проектами. Главная проблема была в том, что само web-приложение и http-сервис 1С располагаются на разных доменах. Хотя не обязательно все организовывать так сложно. Самый просто пример может выглядеть так: web-сервис от 1С на localhost и страничка html открытая как файл. Причем последняя может попробовать обратиться к сервисам 1С и получит, как это принято говорить, "болт".

ВНИМАНИЕ: Прежде чем вы продолжите читать, я сразу предупрежу: вам должно быть понятны такие вещи, как html, javacript. В противном случае, я предлагаю поставить эту статью в избранное и вернуться позже.

CORS наше все

Поддержка XMLHttpRequest с CORS есть не во всех браузерах. Но с учетом количества пользователей Safari 4+, Chrome 3+, IE 10+ вполне достаточно чтобы рассматривать данную технологию, как основную к использованию.

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

Начнем с небольшого куска кода (мне удобнее использовать синтаксис angular, но знающие jquery с легкостью прочитают этот код и перепишут).

$http.post('http://localhost/mybase/hs/inbox/user')
        .success(function(data){
            alert('success');
        })
        .error(function(data){
            alert('error');
        });

Данный запрос (при условии иного домена у инициатора) вызовет ошибки следующего характера:

XMLHttpRequest cannot load http://localhost/mybase/hs/inbox/user. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63342' is therefore not allowed access.

Рассмотрим повнимательнее как сам запрос, так и ответ. И начнем с заголовков. С клиентской стороны все вроде как обычно, но добавлен заголовок с именем домена, откуда делается запрос: Origin: http://localhost:63342

При этом сервер нам вполне доходчиво объясняет что ему не хватает заголовка с определением области доступа (Access-Control-Allow-Origin). Если мы попробуем добавить этот заголовок с указанием "*" в наш ответ, то запрос будет выполнен.

Однако, в свой ответ я добавил еще несколько заголовков для более конкретного описания области доступа. 

	Ответ = Новый HTTPСервисОтвет(200);
	Ответ.Заголовки.Вставить("Access-Control-Allow-Origin", "http://localhost:63342");
	Ответ.Заголовки.Вставить("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, HEAD, OPTIONS");
	Ответ.Заголовки.Вставить("Access-Control-Allow-Credentials", "true");
	Ответ.Заголовки.Вставить("Access-Control-Allow-Headers", "X-Requested-With, origin, content-type, accept");

Такой способ применим, только если в качестве веб-сервера вы используете IIS. Для Apache необходимо делать несколько иначе. В файле httpd.config необходимо внести следующие изменения:

1. Найти строку "#LoadModule headers_module modules/mod_headers.so" и убрать первый символ "#"

2. В раздел "<Directory />" (также при наличии Location, Files) вписать следующее (соответственно указав области доступа более конкретно.):

<IfModule mod_headers.c>
   Header always set Access-Control-Allow-Origin "*"
   Header always set Access-Control-Max-Age "1000"
   Header always set Access-Control-Allow-Headers "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"
   Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
</IfModule>

Вот такое элегантное решение. Без лишних HTTP запросов, без изменения клиентского API.

От автора

Если у вас есть важные дополнения, замечания или критика, пишите, не стесняйтесь, пожалуйста. Все будет учтено, и статья будет дополнена.

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Ivan Khorkov (vano-ekt) 30.08.15 17:43
м.б. пользователю службы апача сделать авторизацию ОС?
2. pahalovo (pahalovo) 31.08.15 05:30
Для IIS можно прописать в web.config (находится каталоге куда публикуется http сервис 1С)
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>

Все заголовки которые перечислены в customHeader будут автоматически добавляться к http-ответам.

Тоже разрабатываю личный кабинет к системе учета заявок на angular.js и http-сервисах 1С, и сейчас застрял на кросс доменной авторизации пользователей.
3. Александр Анисков (vandalsvq) 31.08.15 09:20
(2) pahalovo, я намерено не указал данный метод, поскольку при обновлении публикации 1с меняет файл. А постоянно туда лазить не хочется. Более того, разрешение будет выдано на все сервисы, а это уже определенная брешь в безопасности. В аппаче же другого выхода нет кроме правки настроек веб-сервера. Ну или я что то не умею ))))
4. pahalovo (pahalovo) 01.09.15 01:34
У этого метода есть свои недостатки, но зато это самый быстрый способ включить CORS в существующем api.
Статью можно дополнить описанием механизма "preflight" запросов. В каких случаях эти запросы отправляются и как их правильно обрабатывать на стороне 1С.
Это бы сэкономило много времени тем кто первый раз столкнулся с CORS.

5. Евгений Маляров (unpete) 03.09.15 16:13
(3) vandalsvq,
при таком раскладе авторизация пользователя средствами платформы уже отпадает

1. Что мешает установить в XHR
withCredentials = true
и добавить заголовок
Authorization

2. На самом деле, заголовки
Access-Control...
нужны не всегда, а только при обработке запроса типа
OPTIONS
. Такой минизапрос браузер добавляет перед выполнением реального кроссдоменного запроса. Можно добавлять всегда - ничего страшного в отправке нескольких лишних байт нет.
6. Евгений Маляров (unpete) 03.09.15 16:36
(2) pahalovo,
системе учета заявок на angular.js

angular, backbone, yui, extjs и т.д. знать и использовать, конечно, необходимо. Это - азбука, без которой невозможно разговаривать.
Для построения production веб-приложений, предлагаю ознакомиться с библиотекой metadata.js большинство ui и data - задач решаются там из коробки без кодирования расстановкой галочек и подготовкой файла описания метаданных
7. Александр Анисков (vandalsvq) 03.09.15 20:47
(5) unpete, спасибо за подсказку относительно заголовка Auhorization. Чего-то я не подумал так попробовать. Впрочем в рамках моей задачи не требовалась авторизация под конкретными пользователями. Но в любом случае, спасибо.
8. Евгений Маляров (unpete) 03.09.15 21:35
(7) vandalsvq, На самом деле, CORS с авторизацией несколько сложнее, т.к. сервер должен в этом случае возвращать не "Access-Control-Allow-Origin: *", а "Access-Control-Allow-Origin: Origin инициатора"
imho, правильнее не преодолевать героически самостоятельно созданные проблемы, а выбирать архитектуру, в которой эти проблемы в принципе не возникнут.
У администратора веб-сервера, всегда есть возможность подклеить http://bad.cors.com/ в https://our.site.ru/bad_cors_com/ и работать безо всяких корсов.
Лучше всего для этих целей подходит nginx, но на apache и iis так же, можно настроить.
9. Александр Анисков (vandalsvq) 04.09.15 01:58
(8), вот это я уже не понял.
У администратора веб-сервера, всегда есть возможность подклеить http://bad.cors.com/ в https://our.site.ru/bad_cors_com/ и работать безо всяких корсов.

Видимо не дорос еще ((.

Сейчас я Access-Control-Allow-Origin в принципе сразу с указанием инициатора возвращаю. Думаю так правильнее как бы.
10. Евгений Маляров (unpete) 04.09.15 10:49
(9) vandalsvq, http://nginx.org/ru/docs/beginners_guide.html там по русски. см. директиву proxy_pass
сразу с указанием инициатора возвращаю

В общем случае, задать инициатора статически не получится. К серверу могут обращаться с разных доменов и есть возможность одним разрешать, а других посылать.

А еще, проверку cors можно отключить в браузере, запустив его с определенными параметрами командной строки. Например, для Chrome: "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security
Для того же Chrome, есть директива --allow-cross-origin-auth-prompt - показывает диалог basic-авторизации, если таковая требуется на кроссдоменном сервере.
11. Евгений Маляров (unpete) 04.09.15 10:54
(9) vandalsvq, Еще, вспомнил про serviceWorkers. С их помощью, так же, можно кроссдоменные запросы обрабатывать. Причем, вызывающая страница про serviceWorker-а и кроссдомен может ничего не знать. Её xhr будет думать, что работает с локальным сервером, а serviceWorker подменит данные.
faker1980tyumen; +1 Ответить 1
12. Александр Анисков (vandalsvq) 04.09.15 13:40
(10), (11) unpete, спасибо за все советы. Очень познавательно.
13. Александр Анисков (vandalsvq) 04.09.15 14:50
(10) unpete, что-то мне подсказывает что --disable-web-security не самое лучшее решение. Кстати до кучи еще можно поставить например плагин в хроме, который будет сам заголовок cors подставлять.
14. Евгений Маляров (unpete) 05.09.15 19:58
(13) vandalsvq,
--disable-web-security не самое лучшее решение
Это вовсе не решение для production, но улучшает понимание, что cors - это всего лишь соглашение внутри браузера и к обеспечению реальной безопасности отношения не имеет.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа