Autenticación básica anticipada con Apache HttpClient 4

¿Existe una manera más fácil de configurar el cliente http para la autenticación básica preventiva que la que se describe aquí ?
En la versión anterior (3.x) solía ser una llamada de método simple (por ejemplo, httpClient.getParams().setAuthenticationPreemptive(true) ).
Lo principal que quiero evitar es agregar BasicHttpContext a cada método que ejecuto.

Es difícil hacer esto sin pasar un contexto en todo momento, pero probablemente puedas hacerlo mediante el uso de un interceptor de solicitud. Aquí hay un código que usamos (encontrado en su JIRA, iirc):

 // Pre-emptive authentication to speed things up BasicHttpContext localContext = new BasicHttpContext(); BasicScheme basicAuth = new BasicScheme(); localContext.setAttribute("preemptive-auth", basicAuth); httpClient.addRequestInterceptor(new PreemptiveAuthInterceptor(), 0); (...) static class PreemptiveAuthInterceptor implements HttpRequestInterceptor { public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE); // If no auth scheme avaialble yet, try to initialize it // preemptively if (authState.getAuthScheme() == null) { AuthScheme authScheme = (AuthScheme) context.getAttribute("preemptive-auth"); CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER); HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); if (authScheme != null) { Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort())); if (creds == null) { throw new HttpException("No credentials for preemptive authentication"); } authState.setAuthScheme(authScheme); authState.setCredentials(creds); } } } } 

Si está buscando obligar a HttpClient 4 a autenticarse con una única solicitud, lo siguiente funcionará:

 String username = ... String password = ... UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password); HttpRequest request = ... request.addHeader(new BasicScheme().authenticate(creds, request)); 

Esta es la misma solución que Mat’s Mannion, pero no tiene que poner localContext para cada solicitud. Es más simple, pero agrega autenticación a TODAS las solicitudes. Útil, si no tiene control sobre las solicitudes individuales, como en mi caso al usar Apache Solr, que utiliza HttpClient internamente.

 import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthState; import org.apache.http.auth.Credentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.protocol.ClientContext; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; httpClient.addRequestInterceptor(new PreemptiveAuthInterceptor(), 0); (...) static class PreemptiveAuthInterceptor implements HttpRequestInterceptor { public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE); // If no auth scheme available yet, try to initialize it // preemptively if (authState.getAuthScheme() == null) { CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER); HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort())); if (creds == null) { throw new HttpException("No credentials for preemptive authentication"); } authState.setAuthScheme(new BasicScheme()); authState.setCredentials(creds); } } } 

Por supuesto, debe establecer el proveedor de credenciales:

 httpClient.getCredentialsProvider().setCredentials( new AuthScope(url.getHost(), url.getPort()), new UsernamePasswordCredentials(username, password)) 

AuthScope no debe contener el dominio, ya que no se conoce de antemano.

Muchas de las respuestas anteriores usan código obsoleto. Estoy usando Apache SOLRJ versión 5.0.0. Mi código consiste en

 private HttpSolrClient solrClient; private void initialiseSOLRClient() { URL solrURL = null; try { solrURL = new URL(urlString); } catch (MalformedURLException e) { LOG.error("Cannot parse the SOLR URL!!" + urlString); throw new SystemException("Cannot parse the SOLR URL!! " + urlString, e); } String host = solrURL.getHost(); int port = solrURL.getPort(); AuthScope authScope = new AuthScope(host, port); BasicTextEncryptor textEncryptor = new BasicTextEncryptor(); textEncryptor.setPassword("red bananas in the spring"); String decryptPass = textEncryptor.decrypt(pass); UsernamePasswordCredentials creds = new UsernamePasswordCredentials(userName, decryptPass); CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( authScope, creds); HttpClientBuilder builder = HttpClientBuilder.create(); builder.addInterceptorFirst(new PreemptiveAuthInterceptor()); builder.setDefaultCredentialsProvider(credsProvider); CloseableHttpClient httpClient = builder.build(); solrClient = new HttpSolrClient(urlString, httpClient); } 

El PreemptiveAuthInterceptor ahora es el siguiente:

 static class PreemptiveAuthInterceptor implements HttpRequestInterceptor { public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE); // If no auth scheme available yet, try to initialize it // preemptively if (authState.getAuthScheme() == null) { CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(HttpClientContext.CREDS_PROVIDER); HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST); AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort()); Credentials creds = credsProvider.getCredentials(authScope); if(creds == null){ } authState.update(new BasicScheme(), creds); } } } 

Un poco tarde para la fiesta, pero encontré el hilo intentando resolver esto para la preautorización de una solicitud por correo postal. Para agregar a la respuesta de Adam, encontré que lo siguiente funcionó para mí:

 HttpPost httppost = new HttpPost(url); UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password); Header bs = new BasicScheme().authenticate(creds, httppost); httppost.addHeader("Proxy-Authorization", bs.getValue()); 

Pensamiento que podría ser útil para cualquier otra persona que se encuentre con esto.

Creo que la mejor manera puede ser simplemente hacerlo manualmente. Agregué la siguiente función

Java clásico:

 import javax.xml.bind.DatatypeConverter; 

 private static void addAuthHeader(HttpRequestBase http, String username, String password) throws UnsupportedEncodingException { String encoded = DatatypeConverter.printBase64Binary((username + ":" + password).getBytes("UTF-8")); http.addHeader("AUTHORIZATION", "Basic " + encoded); } 

HTTPRequestBase puede ser una instancia de HttpGet o HttpPost

Androide:

 import android.util.Base64; 

 private static void addAuthHeader(HttpRequestBase http, String username, String password) throws UnsupportedEncodingException { String encoded = Base64.encodeToString((username + ":" + password).getBytes("UTF-8"), Base64.NO_WRAP); http.addHeader("AUTHORIZATION", "Basic " + encoded); } 

Estoy usando este código, basado en mi lectura de los documentos HTTPClient 4.5 :

 HttpClientContext ctx = HttpClientContext.create() ctx.setCredentialsProvider(new BasicCredentialsProvider()) ctx.setAuthCache(new BasicAuthCache()) UsernamePasswordCredentials creds = new UsernamePasswordCredentials(user, pass) AuthScope authScope = new AuthScope(host, port) ctx.getCredentialsProvider.setCredentials(authScope, credentials) // This part makes authentication preemptive: HttpHost targetHost = new HttpHost(host, port, scheme) ctx.getAuthCache.put(targetHost, new BasicScheme()) 

… y asegúrese de pasar siempre ese contexto a HTTPClient.execute() .

No entiendo el comentario de cierre. Es el HttpClient el que tiene toda esa maquinaria para hacer la autenticación preventiva, y solo tienes que hacer eso una vez (cuando construyes y configuras tu HttpClient). Una vez que haya hecho eso, construirá las instancias de su método de la misma manera que siempre. No “agrega BasicHttpContext” al método.

Creo que tu mejor opción es tener tu propio objeto que configure toda la basura requerida para la autorización preventiva, y que tenga un método o métodos simples para ejecutar solicitudes en determinados objetos HTTPMethod.

en Android, la respuesta de Mat Mannion no puede resolver https, todavía envía dos solicitudes, puede hacer lo siguiente, el truco es agregar authHeader con user-agent:

  public static DefaultHttpClient createProxyHttpClient() { try { final DefaultHttpClient client = createPlaintHttpClient(); client.setRoutePlanner(new HttpRoutePlanner() { @Override public HttpRoute determineRoute(HttpHost target, HttpRequest request, HttpContext context) throws HttpException { boolean isSecure = "https".equalsIgnoreCase(target.getSchemeName()); if (needProxy) { Header header = isSecure ? ProxyUtils.createHttpsAuthHeader() : ProxyUtils.createAuthHeader(); if (isSecure) { client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT + "\r\n" + header.getName() + ":" + header.getValue()); } else { client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT); if (request instanceof RequestWrapper) { request = ((RequestWrapper) request).getOriginal(); } request.setHeader(header); } String host = isSecure ? ProxyUtils.SECURE_HOST : ProxyUtils.HOST; int port = isSecure ? ProxyUtils.SECURE_PORT : ProxyUtils.PORT; return new HttpRoute(target, null, new HttpHost(host, port), isSecure); } else { client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT); return new HttpRoute(target, null, isSecure); } } }); return client; } catch (Exception e) { e.printStackTrace(); return new DefaultHttpClient(); } } public static DefaultHttpClient createPlaintHttpClient() { try { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); PlainSSLSocketFactory socketFactory = new PlainSSLSocketFactory(trustStore); socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); BasicHttpParams params = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(params, 30000); HttpConnectionParams.setSoTimeout(params, 30000); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); registry.register(new Scheme("https", socketFactory, 443)); ThreadSafeClientConnManager ccm = new ThreadSafeClientConnManager(params, registry); HttpClientParams.setCookiePolicy(params, CookiePolicy.BROWSER_COMPATIBILITY); final DefaultHttpClient client = new DefaultHttpClient(ccm, params); client.setRoutePlanner(new HttpRoutePlanner() { @Override public HttpRoute determineRoute(HttpHost target, HttpRequest arg1, HttpContext arg2) throws HttpException { client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT); return new HttpRoute(target, null, "https".equalsIgnoreCase(target.getSchemeName())); } }); return client; } catch (Exception e) { e.printStackTrace(); return new DefaultHttpClient(); } }