2011-05-06 28 views
8

Próbuję użyć Apache/Jakarta HttpClient 4.1.1 do połączenia się z dowolną stroną internetową przy użyciu podanych poświadczeń. Aby to przetestować, mam minimalną instalację IIS 7.5 na mojej maszynie dev, gdzie działa tylko jeden tryb uwierzytelniania na raz. Uwierzytelnianie podstawowe działa dobrze, ale Digest i zwrot 401 komunikaty o błędach NTLM gdy próbuję się zalogować Oto mój kod.HttpClient 4.1.1 zwraca 401 podczas uwierzytelniania za pomocą NTLM, przeglądarki działają dobrze

DefaultHttpClient httpclient = new DefaultHttpClient(); 
    HttpContext localContext = new BasicHttpContext(); 
    HttpGet httpget = new HttpGet("http://localhost/"); 
    CredentialsProvider credsProvider = new BasicCredentialsProvider(); 
    credsProvider.setCredentials(AuthScope.ANY, 
      new NTCredentials("user", "password", "", "localhost")); 
    if (!new File(System.getenv("windir") + "\\krb5.ini").exists()) { 
     List<String> authtypes = new ArrayList<String>(); 
     authtypes.add(AuthPolicy.NTLM); 
     authtypes.add(AuthPolicy.DIGEST); 
     authtypes.add(AuthPolicy.BASIC); 
     httpclient.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF, 
       authtypes); 
     httpclient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF, 
       authtypes); 
    } 
    localContext.setAttribute(ClientContext.CREDS_PROVIDER, credsProvider); 
    HttpResponse response = httpclient.execute(httpget, localContext); 
    System.out.println("Response code: " + response.getStatusLine()); 

Jedna rzecz zauważyłem w Skrzypek jest to, że hashe wysłane przez Firefoksa versus przez HttpClient są różne, dzięki czemu myślę, że być może IIS 7.5 spodziewa się silniejszego mieszania niż zapewnia HttpClient? Jakieś pomysły? Byłoby wspaniale, gdybym mógł sprawdzić, czy to zadziała z NTLM. Digest też byłby fajny, ale w razie potrzeby mogę żyć bez niego.

+0

Mam uwierzytelnianie Digest do pracy w przeglądarkach, ale nadal pokazuje 401 zabronione w HttpClient. Jestem zdumiony. – Jesse

+0

Kod działa dla mnie, jednak jest przestarzały w 4.3. Nie mogę znaleźć jasnego przewodnika na temat używania czystego kodu 4.3. – Paul

Odpowiedz

7

nie jestem ekspertem w tej sprawie, ale w trakcie uwierzytelniania NTLM, używając komponentów http Widziałem, że klient potrzebuje 3 próby w celu podłączenia do punktu końcowego NTML w moim przypadku. Jest to nieco opisane dla Spnego, ale jest nieco inne dla uwierzytelniania NTLM.

Dla NTLM w pierwszym klientem próba będzie złożyć wniosek o Target auth state: UNCHALLENGED i serwer WWW zwraca HTTP 401 statusu oraz nagłówek: WWW-Authenticate: NTLM

Klient sprawdzi skonfigurowanych systemów uwierzytelniania, NTLM powinien być skonfigurowany w kodzie klienta.

Druga próba, klient złoży wniosek o Target auth state: CHALLENGED i wyśle ​​nagłówek autoryzacji z tokena zakodowany w formacie base64: Authorization: NTLM TlRMTVNTUAABAAAAAYIIogAAAAAoAAAAAAAAACgAAAAFASgKAAAADw== Server ponownie zwraca HTTP 401 statusu ale nagłówek: WWW-Authenticate: NTLM teraz jest wypełniona zakodowanej informacji.

3rd Próba Klient użyje informacji z nagłówka WWW-Authenticate: NTLM i wykona ostateczne żądanie z Target auth state: HANDSHAKE i nagłówkiem autoryzacji Authorization: NTLM, który zawiera więcej informacji dla serwera.

Po tym otrzymam po HTTP/1.1 200 OK.

Aby tego uniknąć w każdym żądaniu documentation w rozdziale 4.7.1 stwierdza się, że ten sam token wykonania musi być używany dla logicznie powiązanych żądań. Dla mnie to nie zadziałało.

Mój kod: zainicjować klienta raz w @PostConstruct metody 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(); 

używać tej samej instancji klienta w każdej życzenie:

  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); 

zwalnianie zasobów i powrót połączenia wróć do menedżera połączeń, używając metody @PreDestroy mojego EJB:

   this.httpclient.close(); 
2

Najprostszym sposobem rozwiązania takich sytuacji, które znalazłem, jest Wireshark. Jest to bardzo duży młotek, ale naprawdę pokaże ci wszystko. Zainstaluj go, upewnij się, że twój serwer znajduje się na innej maszynie (nie działa z Localhost) i zacznij rejestrować.

Uruchom żądanie, które się nie powiedzie, uruchom to, które działa. Następnie filtruj według http (wystarczy wstawić http w polu filtru), znajdź pierwsze żądanie GET, znajdź inne żądanie GET i porównaj. Zidentyfikuj znaczącą różnicę, masz teraz określone słowa kluczowe lub problemy do wyszukiwania kodu/sieci dla. Jeśli nie wystarczy, zawęź do pierwszej rozmowy TCP i spójrz na pełne żądanie/odpowiedź. To samo z drugim.

Rozwiązałem z tym podejściem niewiarygodną liczbę problemów. A Wireshark jest bardzo przydatnym narzędziem do poznania. Wiele super zaawansowanych funkcji ułatwiających debugowanie sieci.

Można również uruchomić go na końcu klienta lub serwera. Cokolwiek będzie pokazywać obie prośby o porównanie.

1

W końcu to rozgryzłem. Uwierzytelnianie za pomocą szyfrowania wymaga, aby w przypadku pełnego adresu URL w żądaniu proxy musiał także użyć pełnego adresu URL. Nie zostawiłem kodu proxy w próbie, ale został skierowany do "localhost", który spowodował niepowodzenie. Zmiana tego na 127.0.0.1 sprawiła, że ​​działało.

3

Miałem podobny problem z HttpClient 4.1.2. Dla mnie został rozwiązany poprzez powrót do HttpClient 4.0.3. Nigdy nie mogłem uzyskać dostępu do NTLM z 4.1.2 przy użyciu wbudowanej implementacji lub przy użyciu JCIFS.

+0

Tylko notatkę, wpadłem na ten sam problem, jak również. Wydanie httpclient 4.2.3 twierdzi, że ma odświeżoną implementację NTLM. Odkryłem, że przejście na wersję 4.2.3 przebiega bez zakłóceń w odniesieniu do NTLM. –

4

miałem ten sam problem z HttpClient4.1.X Po zaktualizowaniu go do HttpClient 4.2.6 to woked jak uroku. Poniżej jest mój kod

DefaultHttpClient httpclient = new DefaultHttpClient(); 
     HttpContext localContext = new BasicHttpContext(); 
     HttpGet httpget = new HttpGet("url"); 
     CredentialsProvider credsProvider = new BasicCredentialsProvider(); 
     credsProvider.setCredentials(AuthScope.ANY, 
       new NTCredentials("username", "pwd", "", "domain")); 
        List<String> authtypes = new ArrayList<String>(); 
      authtypes.add(AuthPolicy.NTLM);  
      httpclient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF,authtypes); 

     localContext.setAttribute(ClientContext.CREDS_PROVIDER, credsProvider); 
     HttpResponse response = httpclient.execute(httpget, localContext); 
     HttpEntity entity=response.getEntity(); 
+0

Ten kod pracował dla mnie z wersją 4.2.2 ... Pozdrawiam !!! –

2

Uaktualnienie naszej aplikacji do korzystania ze słoików w httpcomponent-client-4.5.1 rozwiązało dla mnie ten problem.