Челленджи интеграции Azure BlobStorage с Salesforce Commerce Cloud
Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті
Всем привет. Меня зовут Иван Стариков, три года я работаю с платформой электронной коммерции Salesforce Commerce Cloud (далее SFCC, кратко о платформе можно почитать в этой статье), в роли веб-разработчика в компании Astound Commerce. За время работы пришлось работать с разными 3rd-party сервисами, интегрировать их. Это были различные службы доставки, платежные методы, поиск адресов и т.п. Некоторые интеграции вполне тривиальны, «обкатаны», но некоторые отличаются тем, что приходится быть первопроходцем. В этой статье хочу рассказать как раз о такой интеграции. Статья будет интересна разработчикам в срезе разных подходов к интеграции сервиса облачного хранения Azure, а также непосредственно SFCC-разработчикам.
Постановка задачи
Мы получили запрос от клиента, чтобы организовать внешнее хранилище данных для хранения файлов пользователя, а именно речь идет о хранении изображений для персонализации продукта пользователем.
Ограничения платформы SFCC
Платформа SFCC не подразумевает хранилища для файлов, доступных извне. Естественно файловое хранилище существует, но целенаправленно используется для «внутренних» нужд системы. Оно ограничено по размерам, производительности и, главное, — безопасности. Доступа извне к этому хранилищу нет. То есть, например, мы можем записать файл из контроллера, но чтобы прочитать этот файл или отобразить на клиентской стороне — нам придется в ручном режиме возвращать файл, что ресурсозатратно (дополнительный вызов на бекенд), неоптимально и затратно по времени.
Изображение 1. Пример структуры каталога Impex
Обоснование решения
Принимая во внимание требования к возможности персонализации продуктов — изображение должно быть загружено/отображено буквально сразу. И у пользователя должна быть возможность загрузить разные изображения, «примерить» их, и выбрать, что же в итоге будет по нраву.
Ввиду вышеописанных ограничений, было принято решение использовать облачное хранилище. У клиента выбор пал на Microsoft Azure, поскольку проще расширить существующую подписку, чем открывать новую.
Разбираемся с Azure SDK
Начинать интеграцию лучше с ознакомления с официальной документацией и SDK
Изображение 2. Azure SDK for JS
SDK состоит из 2х частей — клиентской и серверной. В нашем случае серверной частью воспользоваться не удастся, потому что она рассчитана на запуск в среде Node.JS. Серверная часть SFCC хоть и понимает JS (ECMAScript 5), но не позволяет запускать NodeJS модули.
Стандартным решением проблемы в таком случае является создание Сервиса для общения с Azure API.
Изображение 3. Пример сервиса для взаимодействия с Azure API
И вот тут стоит остановиться подробнее на самом Azure и методах взаимодействия с ним.
Документация описывает несколько вариантов аутентификации. В нашем случае решение будет основано на Blob Storage (Azure Blobs на скриншоте).
Изображение 4. Типы облачного хранилища Azure
Выбор из доступных вариантов аутентификации
Есть следующие варианты авторизации:
- Shared Key — авторизация по заголовкам http-запроса
- SAS — авторизация по специальным параметрам в url
- Active Directory
- Открытый доступ для чтения изображений
Последнее мы не будем рассматривать — сервера SFCC будут вне домена AD, а вариант с открытым доступом для чтения нас также не устроит ввиду конфиденциальности загружаемых изображений.
Shared Key. Алгоритм формирования подобных заголовков для авторизации хорошо задокументирован (docs.microsoft.com/...authorize-with-shared-key).
Необходимо передать заголовок авторизации в http запросе
Authorization="[SharedKey|SharedKeyLite] <AccountName>:<Signature>"
Строка signature формируется следующим образом:
1. Сформировать строку для подписи по следующему алгоритму
StringToSign = VERB + "\n" + Content-Encoding + "\n" + Content-Language + "\n" + Content-Length + "\n" + Content-MD5 + "\n" + Content-Type + "\n" + Date + "\n" + If-Modified-Since + "\n" + If-Match + "\n" + If-None-Match + "\n" + If-Unmodified-Since + "\n" + Range + "\n" + CanonicalizedHeaders + CanonicalizedResource;
2. Закодировать ее
Signature=Base64(HMAC-SHA256(UTF8(StringToSign), Base64.decode(<your_azure_storage_account_shared_key>)))
Всё, можем приступать — имплементация простая, обычный сервис для общения посредством REST API, создали заголовки, добавили в запрос, отправили/получили данные. Успех? В ходе разработки выясняется — есть квота (ввиду контроля производительности, в SFCC встроены ограничения на разные ресурсоемкие операции)
Quota api.dw.net.HTTPClient.send()@SF (enforced, warn 5, limit 8): limit exceeded 1 time(s), max actual was 9
Метод, используемый при отправке данных сервисом — HTTPClient.send()
В рамках одного HTTP-запроса к сервису мы ограничены отправкой 8 файлов. Хм. При этом пользователю доступна условно-неограниченная возможность загрузки изображений (несколько за раз, перезаливка и т.п.).
Хорошо, для нас есть еще один вариант авторизации.
Shared Access Signature. Это набор get-параметров с токеном доступа. Выглядит вот так:
?sv=2012-02-12&st=2009-02-09&se=2009-02-10&sr=c&sp=r&si=YWJjZGVmZw%3d%3d&sig=dD80ihBh5jfNpymO5Hg1IdiJIEvHcJpCMiCMnN%2fRnbI%3d
В SDK есть возможность использования SAS токена
Изображение 5. Описание доступа к Blob клиенту с помощью SAS токена
Таким образом, нам необходимо «пробросить» это токен с бэкенда на фронтенд, и при инициализации BlobClient использовать его.
Особенности использования SAS токенов
Например, безопасность. Нам необходимо защитить токен по ip. Нежелательно его передавать в открытом виде, ведь, перехватив его, можно как угодно управлять хранилищем (в рамках полномочий токена).
Изображение 6. Генерация SAS токена на портале Azure с привязкой к IP
Пойдем немного дальше, и имплементируем генерацию токена для каждого отдельного файла.
var specificUriRequest = [ X_MS_SAS_SP, // signedPermissions dateStart, // signedStart dateEnd, // signedExpiry canonicalizedResource + specificUri, // canonicalizedResource '', // signedIdentifier serverIp, // signedIP X_MS_SAS_SPR, // signedProtocol X_MS_VERSION, // signedVersion X_MS_SAS_SR, // signedResource '', // signedSnapshotTime '', // rscc '', // rscd '', // rsce '', // rscl '' // rsct ]; var stringToSignUri = specificUriRequest.map(function (el) { return (StringUtils.trim(el)); }).join('\n'); var signatureBytesUri = mac.digest(stringToSignUri, Encoding.fromBase64(azureKey)); var signatureEncodedUri = Encoding.toBase64(signatureBytesUri);
Фрагмент кода 1. Генерация токена для доступа к конкретному файлу
Таким образом, даже если токен и будет перехвачен, то его TTL ограничено, и он может быть использован лишь для доступа к конкретному файлу, и снова-таки ограничение использования по IP.
Токен формируем при загрузке изображения и далее уже его используем для доступа со стороны клиентской части.
Что можно было улучшить?
Рассматривали вариант имплементации некоего подобия lazy-loader для загрузки изображений в виде base64, но ввиду снижения производительности в таком случае от этого отказались.
Также генерация токена для каждого изображения тоже не самое оптимальное решение. Как вариант, рассматривается временный токен для взаимодействия с папкой пользователя на короткий промежуток времени и с привязкой по ip.
Вместо заключения
Документация по работе с Azure достаточно обширна и хорошо расписана, но есть ряд нюансов, которые нужно просто знать. Это касается генерации токенов, для разных типов доступа — к blob-сущности, контейнеру или сервису в целом. Поэтому какое-то время пришлось затратить чтобы разобраться в этих самых нюансах.
Реализация получилась в чем-то сложнее, чем ожидалось, но достаточно стабильная и соответствующая требованиям и ожиданиям заказчика.
Добавлю также, что простая на первый взгляд интеграция может превратиться в челлендж ввиду некоторых особенностей платформы.
3 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів