Java-сервис интеграции с SharePoint Online через REST API
При разработке корпоративных информационно-управляющих систем (КИУС) часто стоит задача оптимизировать работу с разнообразными реестрами — наборами однотипных данных, взаимодействие с которыми сводится к заполнению и редактированию многочисленных форм. Проектирование, согласование и разработка этих форм может занять несколько недель, что отодвигает начало эксплуатации системы.

Решения, подобные SharePoint Online, способствуют быстрому запуску подсистемы ведения реестров, поскольку предоставляют готовый функционал для построения форм и хранения данных в различных форматах, возможности по разграничению доступа и интеграции и многое другое. Разработчикам остается реализовать механизм манипулирования данными в SharePoint посредством, например, REST API — именно такое решение мы использовали в одном из проектов.
Аутентификация
SharePoint Online придерживается жестких стандартов безопасности, поэтому аутентификация для работы с REST API состоит из трех шагов:
1. Получение security token с единого портала аутентификации Microsoft:
public String receiveSecurityToken() throws TransformerException, URISyntaxException { RequestEntity<String> requestEntity = new RequestEntity<>(buildSecurityTokenRequestEnvelope(), HttpMethod.POST, new URI("https://login.microsoftonline.com/extSTS.srf")); ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); DOMResult result = new DOMResult(); Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.transform(new StringSource(responseEntity.getBody()), result); Document definitionDocument = (Document) result.getNode(); String securityToken = xPathExpression.evaluateAsString(definitionDocument); if (StringUtils.isBlank(securityToken)) { throw new SharePointAuthenticationException("Unable to authenticate: empty token"); } return securityToken; }
Конверт, отправляемый на портал, имеет следующий формат:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"> <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>[username]</o:Username> <o:Password>[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>[SharePoint domain address]</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>
2. Получить cookies с сервера SharePoint Online:
public List<String> getSignInCookies(String securityToken) throws TransformerException, URISyntaxException { RequestEntity<String> requestEntity = new RequestEntity<>(securityToken, HttpMethod.POST, new URI("[SharePoint domain address]/_forms/default.aspx?wa=wsignin1.0")); ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); HttpHeaders headers = responseEntity.getHeaders(); List<String> cookies = headers.get("Set-Cookie"); if (CollectionUtils.isEmpty(cookies)) { throw new SharePointSignInException("Unable to sign in: no cookies returned in response"); } return cookies; }
3. Получить подпись для запросов на сервер SharePoint Online:
public String getFormDigestValue(List<String> cookies) throws IOException, URISyntaxException, TransformerException, JSONException { MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); headers.add("Cookie", Joiner.on(';').join(cookies)); headers.add("Accept", "application/json;odata=verbose"); headers.add("X-ClientService-ClientTag", "SDK-JAVA"); RequestEntity<String> requestEntity = new RequestEntity<>(headers, HttpMethod.POST, new URI("[SharePoint domain address]/_api/contextinfo")); ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); JSONObject json = new JSONObject(responseEntity.getBody()); return json.getJSONObject("d").getJSONObject("GetContextWebInformation").getString("FormDigestValue"); }
Выполнение запросов на сервер SharePoint Online
Теперь мы имеем все необходимое для выполнения запросов на сервер SharePoint Online. В заголовки запроса необходимо добавить полученные cookies, выставить требуемый Content-Type и добавить подпись. В общем случае, метод выглядит следующим образом:
public String performHttpRequest(String path, String json, boolean isUpdate) throws Exception { String securityToken = service.receiveSecurityToken(); List<String> cookies = service.getSignInCookies(securityToken); String formDigestValue = service.getFormDigestValue(cookies); MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); headers.add("Cookie", Joiner.on(';').join(cookies)); headers.add("Content-type", "application/json;odata=verbose"); headers.add("X-RequestDigest", formDigestValue); if (isUpdate) { headers.add("X-HTTP-Method", "MERGE"); headers.add("IF-MATCH", "*"); } RequestEntity<String> requestEntity = new RequestEntity<>(json, headers, HttpMethod.POST, new URI(path)); ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); return responseEntity.getBody(); }
Необходимо отметить, что security token обладает временем действия в одни сутки, что дает возможность оптимизировать работу с сервисом через кэш.
Заключение
SharePoint Online, даже несмотря на имеющиеся ограничения и сложности в интеграции, дает множество полезных возможностей и готовую инфраструктуру. Это позволяет снизить временные издержки на запуск проектов, требующих обработки большого количества табличных данных.
Если в текущем проекте у Вас есть задача оптимизировать работу с реестрами, или Вам требуется разработка корпоративной информационно-управляющей системы, напишите нам на hello@wave-access.com.
Расскажем подробнее о наших проектах!
Свяжитесь с нами:
hello@wave-access.com
+1 866 311 24 67 (США)
+45 20 55 6222 (Дания)
+49 721 957 3177 (Германия)