Я не эксперт в данной области, но во время аутентификации NTLM с использованием компонентов http я увидел, что клиенту требуется 3 попытки для подключения к конечной точке NTML в моем случае.Это как бы описано здесь для Spnego, но немного отличается для аутентификации NTLM.
Для NTLM в первой попытке клиент сделает запрос с Target auth state: UNCHALLENGED
, и веб-сервер вернет HTTP401 статус и заголовок: WWW-Authenticate: NTLM
Клиент проверит настроенные схемы аутентификации, NTLM должен быть настроен в коде клиента.
Вторая попытка, клиент сделает запрос с Target auth state: CHALLENGED
и отправит заголовок авторизации с токеном, закодированным в формате base64: Authorization: NTLM TlRMTVNTUAABAAAAAYIIogAAAAAoAAAAAAAAACgAAAAFASgKAAAADw==
Сервер снова возвращает статус HTTP 401, но заголовок: WWW-Authenticate: NTLM
теперь заполнен закодированной информацией.
3-я попытка клиента будет использовать информацию из заголовка WWW-Authenticate: NTLM
и сделает окончательный запрос с Target auth state: HANDSHAKE
и заголовком авторизации Authorization: NTLM
, который содержит дополнительную информацию для сервера.
В моем случае после этого я получаю HTTP/1.1 200 OK
.
Во избежание всего этого в каждом запросе Документация в главе 4.7.1 гласит, что такое же выполнениетокен должен использоваться для логически связанных запросов.Для меня это не сработало.
Мой код: я инициализирую клиента один раз в @PostConstruct
методе EJB
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(18);
cm.setDefaultMaxPerRoute(6);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(30000)
.setConnectTimeout(30000)
.setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM))
.setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
.build();
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new NTCredentials(userName, password, hostName, domainName));
// Finally we instantiate the client. Client is a thread safe object and can be used by several threads at the same time.
// Client can be used for several request. The life span of the client must be equal to the life span of this EJB.
this.httpclient = HttpClients.custom()
.setConnectionManager(cm)
.setDefaultCredentialsProvider(credentialsProvider)
.setDefaultRequestConfig(requestConfig)
.build();
Используйте один и тот же экземпляр клиента в каждом запросе:
HttpPost httppost = new HttpPost(endPoint.trim());
// HttpClientContext is not thread safe, one per request must be created.
HttpClientContext context = HttpClientContext.create();
response = this.httpclient.execute(httppost, context);
Освободите ресурсы и верните соединение обратно в диспетчер соединений с помощью метода @PreDestroy моего EJB:
this.httpclient.close();