2011-05-06 16 views
8

Estoy tratando de utilizar el Apache/Jakarta HttpClient 4.1.1 para conectarme a una página web arbitraria usando las credenciales proporcionadas. Para probar esto, tengo una instalación mínima de IIS 7.5 en mi máquina de desarrollo ejecutándose donde solo está activo un modo de autenticación a la vez. La autenticación básica funciona bien, pero Digest y NTLM de retorno 401 mensajes de error cada vez que intente identificarse Aquí está mi código:.HttpClient 4.1.1 devuelve 401 al autenticarse con NTLM, los navegadores funcionan bien

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

La única cosa que he notado en Fiddler es que los valores hash enviado por Firefox por frente HttpClient es diferente, lo que me hace pensar que tal vez IIS 7.5 está esperando hashing más fuerte que HttpClient? ¿Algunas ideas? Sería genial si pudiera verificar que esto funcionaría con NTLM. El resumen sería bueno también, pero puedo vivir sin eso si es necesario.

+0

Obtuve autenticación Digest para que funcione en navegadores, pero aún muestra 401 prohibido en HttpClient. Estoy perplejo. – Jesse

+0

El código funciona para mí, sin embargo, está en desuso en 4.3. No puedo encontrar una guía clara para usar el código puro 4.3. – Paul

Respuesta

7

yo no soy un experto en el tema, pero durante la autenticación NTLM usando componentes http he visto que el cliente necesita 3 intentos con el fin de conectarse a un punto final NTML en mi caso. Se describe como here para Spnego, pero es un poco diferente para la autenticación NTLM.

Para NTLM en el primer cliente intento hará una solicitud con Target auth state: UNCHALLENGED y el servidor Web devuelve HTTP 401 de estado y un encabezado: WWW-Authenticate: NTLM

cliente comprobará los esquemas de autenticación configurados, NTLM se debe configurar en el código de cliente.

segundo intento, el cliente va a hacer una solicitud con Target auth state: CHALLENGED, y enviará una cabecera de autorización con un token codificado en formato base64: Authorization: NTLM TlRMTVNTUAABAAAAAYIIogAAAAAoAAAAAAAAACgAAAAFASgKAAAADw== Server de nuevo vuelve HTTP estado 401, pero el encabezado: WWW-Authenticate: NTLM ahora se rellena con información codificada.

tercera Cliente Intento utilizará la información de cabecera y WWW-Authenticate: NTLM hará la solicitud final con Target auth state: HANDSHAKE y una cabecera de autorización Authorization: NTLM que contiene más información para el servidor.

En mi caso, recibo un HTTP/1.1 200 OK después de eso.

Para evitar todo esto en cada solicitud, documentation en el capítulo 4.7.1 indica que el mismo token de ejecución se debe usar para las solicitudes lógicamente relacionadas. Para mí no funcionó.

Mi código: Me inicializar el cliente una vez en un método de un 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(); 

utilizar la misma instancia del cliente en cada petición @PostConstruct:

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

desasignar los recursos y devolver la conexión volver al administrador de conexión, en el método @PreDestroy de mi EJB:

   this.httpclient.close(); 
2

La manera más fácil de solucionar estas situaciones que encontré es Wireshark. Es un martillo muy grande, pero realmente te mostrará todo. Instálelo, asegúrese de que su servidor esté en otra máquina (no funciona con Localhost) y comience a registrarlo.

Ejecute su solicitud que falla, ejecute una que funcione. Luego, filtre por http (simplemente ponga http en el campo de filtro), encuentre la primera solicitud GET, encuentre la otra solicitud GET y compárela. Identifique la diferencia significativa, ahora tiene palabras clave específicas o problemas para buscar el código/net. Si no es suficiente, acceda a la primera conversación TCP y observe la solicitud/respuesta completa. Lo mismo con el otro.

He resuelto un número increíble de problemas con ese enfoque. Y Wireshark es una herramienta muy útil para saber. Muchas funciones súper avanzadas para facilitar la depuración de su red.

También puede ejecutarlo en el extremo del cliente o del servidor. Lo que sea que le muestre ambas solicitudes le permitirá comparar.

1

Finalmente lo descubrí. La autenticación implícita requiere que si usa una URL completa en la solicitud, el proxy también necesita usar la URL completa. No dejé el código proxy en la muestra, pero fue dirigido a "localhost", lo que provocó que fallara. Cambiar esto a 127.0.0.1 lo hizo funcionar.

3

Tuve un problema similar con HttpClient 4.1.2. Para mí, se resolvió volviendo a HttpClient 4.0.3. Nunca podría lograr que NTLM trabaje con 4.1.2 utilizando la implementación integrada o el uso de JCIFS.

+0

Solo una nota, me encontré con este mismo problema también. La versión httpclient 4.2.3 afirma tener una implementación NTLM renovada. Descubrí que la actualización a 4.2.3 funcionaba perfectamente con respecto a NTLM. –

4

tuve el mismo problema con HttpClient4.1.X Después de actualizar a HttpClient 4.2.6 woked como encanto. A continuación se muestra el código

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

Este código funcionó para mí con la versión 4.2.2 ... ¡Saludos! –

2

La actualización de nuestra aplicación para usar los archivos jar en httpcomponents-client-4.5.1 resolvió este problema para mí.

Cuestiones relacionadas