Блог

Полезные статьи и новости о жизни WaveAccess

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.

Заказать звонок

Удобное время:

Отменить

Пишите!

Присоединить
Файл не больше 30 Мб.
Отменить