java authentication proxy basic-authentication http-proxy

java - La autenticación de proxy básica para las URL de HTTPS devuelve la autenticación de proxy HTTP/1.0 407 requerida



authentication basic-authentication (4)

¿Puedes usar HttpsUrlConnection? Extiende HttpUrlConnection, por lo que la conversión a HttpUrlConnection puede estar bien al regresar de la clase.

El código es similar, en lugar de HttpUrlConnection, use uno con https en el nombre.

Usa el siguiente código:

if (testUrlHttps.getProtocol().toLowerCase().equals("https")) { trustAllHosts(); HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); https.setHostnameVerifier(DO_NOT_VERYFY); urlCon = https; } else { urlCon = (HttpURLConnection) url.openConnection(); }

Fuentes:

[1] https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/HttpsURLConnection.html

[2] HttpURLConnection - "https: //" frente a "http: //" (fragmento de código)

Quiero usar un proxy con autenticación básica (nombre de usuario, contraseña) para una conexión (y solo esta conexión) en Java. El siguiente código funciona para las URL de HTTP (por ejemplo, " http://www.google.com "):

URL url = new URL("http://www.google.com"); HttpURLConnection httpURLConnection = null; InetSocketAddress proxyLocation = new InetSocketAddress(proxyHost, proxyPort); Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyLocation); httpURLConnection = (HttpURLConnection) url.openConnection(proxy); // Works for HTTP only! Doesn''t work for HTTPS! String encoded = new sun.misc.BASE64Encoder().encodeBuffer((proxyUserName + ":" + proxyPassword).getBytes()).replace("/r/n", ""); httpURLConnection.setRequestProperty("Proxy-Authorization", "Basic " + encoded); InputStream is = httpURLConnection.getInputStream(); InputStreamReader isr = new InputStreamReader(is); int data = isr.read(); while(data != -1){ char c = (char) data; data = isr.read(); System.out.print(c); } isr.close();

Sin embargo, el código no funciona para las URL de HTTPS (por ejemplo, " https://www.google.com "). Obtengo java.io.IOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.0 407 Proxy Authentication Required" java.io.IOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.0 407 Proxy Authentication Required" cuando intento acceder a una URL de HTTPS.

Este código funciona para HTTP y HTTPS:

URL url = new URL("https://www.google.com"); HttpURLConnection httpURLConnection = null; InetSocketAddress proxyLocation = new InetSocketAddress(proxyHost, proxyPort); Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyLocation); httpURLConnection = (HttpURLConnection) url.openConnection(proxy); // Works for HTTP and HTTPS, but sets a global default! Authenticator.setDefault(new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(proxyUserName, proxyPassword.toCharArray()); } }); InputStream is = httpURLConnection.getInputStream(); InputStreamReader isr = new InputStreamReader(is); int data = isr.read(); while(data != -1){ char c = (char) data; data = isr.read(); System.out.print(c); } isr.close();

El problema con el segundo código es que establece un nuevo Authenticator predeterminado y no quiero hacer eso, porque este proxy solo lo utiliza una parte de la aplicación y una parte diferente de la aplicación podría estar usando un proxy diferente. No quiero establecer un valor predeterminado global para toda la aplicación. ¿Hay alguna forma de hacer que el primer código funcione con HTTPS o una forma de usar un Authenticator sin establecerlo como predeterminado?

Tengo que usar java.net.HttpURLConnection , porque estoy anulando un método de una clase que tiene que devolver un HttpURLConnection , por lo que no puedo usar Apache HttpClient.


Desafortunadamente, no hay una solución simple para lo que estás tratando de lograr. Su primer código no funciona con HTTPS porque está configurando el encabezado de autenticación directamente. Dado que el cliente cifra todos los datos, el servidor proxy no tiene forma de extraer ninguna información de la solicitud.

De hecho, los servidores HTTPS y proxy funcionan de manera opuesta. El servidor proxy desea ver todos los datos que fluyen entre el cliente y el servidor final y tomar medidas en función de lo que ve. Por otro lado, el protocolo HTTPS encripta todos los datos para que nadie pueda verlos hasta que llegue al destino final. El algoritmo de cifrado se negocia entre el cliente y el destino final para que el servidor proxy no pueda descifrar ninguna información, de hecho, ni siquiera puede saber qué protocolo está usando el cliente.

Para usar un servidor proxy en una conexión HTTPS, el cliente debe establecer un túnel. Para hacer esto, tiene que emitir un comando CONECTAR directamente al proxy, por ejemplo:

CONNECT www.google.com:443 HTTP/1.0

y enviar las credenciales para autenticar con el servidor proxy.

Si la conexión es exitosa, el cliente puede enviar y recibir datos a través de la conexión. El servidor proxy es completamente ciego a los datos. Los datos solo pasan a través de él en su camino entre el cliente y el servidor.

Cuando ejecuta url.openConnection(proxy) en una URL HTTP, devuelve una instancia de HttpURLConnection , cuando se ejecuta en una URL HTTPS como en su segundo código, vuelve a aparecer una instancia de HttpsURLConnection .

Está recibiendo el código de error 407 porque el servidor proxy no puede extraer la información de autenticación del encabezado que envió. Mirando la pila de excepciones, podemos ver que la excepción se lanza en sun.net.www.protocol.http.HttpURLConnection.doTunneling() que emite el comando CONECTAR para establecer el túnel HTTPS a través del proxy. En el código fuente de sun.net.www.protocol.http.HttpURLConnection podemos ver:

/* We only have a single static authenticator for now. * REMIND: backwards compatibility with JDK 1.1. Should be * eliminated for JDK 2.0. */ private static HttpAuthenticator defaultAuth;

Por lo tanto, parece que el autenticador predeterminado es la única forma de proporcionar las credenciales de proxy.

Para hacer lo que quiere, tendría que bajar al nivel de conexión y manejar el protocolo HTTP usted mismo, porque tiene que hablar con el servidor proxy, no directamente con el servidor de Google.


Ok esto es lo que necesitas hacer,

public class ProxyAuth extends Authenticator { private PasswordAuthentication auth; ProxyAuth(String user, String password) { auth = new PasswordAuthentication(user, password == null ? new char[]{} : password.toCharArray()); } protected PasswordAuthentication getPasswordAuthentication() { return auth; } }

.

public class ProxySetup { public HttpURLConnection proxySetup(String urlInput) { URL url; try { url = new URL(urlInput); Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("10.66.182.100", 80)); // or whatever your proxy is HttpURLConnection uc = (HttpURLConnection)url.openConnection(proxy); System.setProperty("https.proxyHost", "10.66.182.100"); System.setProperty("https.proxyPort", "80"); System.setProperty("http.proxyHost", "10.66.182.100"); System.setProperty("http.proxyPort", "80"); String encoded = new String(Base64.encodeBase64(("domain//Username" + ":" + "Password").getBytes())); uc.setRequestProperty("Proxy-Authorization", "Basic " + encoded); Authenticator.setDefault(new ProxyAuth("domain//Username", "Password")); System.out.println("ProxySetup : proxySetup"); return uc; } catch (Exception e) { // TODO Auto-generated catch block System.out.println("ProxySetup : proxySetup - Failed"); e.printStackTrace(); } return null; } }

Utilízalo como.

HttpURLConnection conn = new ProxySetup().proxySetup(URL)


Puede extender ProxiedHttpsConnection y manejar todas las cosas relacionadas de bajo nivel por usted mismo.

Deben realizarse los siguientes pasos para establecer una conexión a través de un proxy HTTP a un sitio web https:

Nota: la comunicación con el servidor proxy y http debe estar en ASCII7 .

  1. Envíe CONNECT .com:443 HTTP/1.0/r/n al proxy
  2. Envíe su autenticación: Proxy-Authorization: Basic c2F5WW91SGF2ZVNlZW5UaGlzSW5UaGVDb21tZW50cw==/r/n .
  3. Finaliza la primera solicitud: /r/n
  4. Lea la respuesta del proxy hasta que vea la combinación "/ r / n / r / n".
  5. Analice la primera línea de la respuesta que recibió del proxy y verifique si comienza con HTTP/1.0 200 .
  6. Inicie una sesión SSL en lugar de la conexión existente.
  7. Envíe el inicio de una solicitud http: GET /questions/3304006/persistent-httpurlconnection-in-java HTTP/1.0/r/n
  8. Establezca el encabezado de host adecuado: Host: .com/r/n
  9. Finalice la solicitud al servidor http: /r/n
  10. Lea till /r/n y analice la primera línea como mensaje de estado
  11. Lea hasta el final de la secuencia para el cuerpo de la solicitud

Cuando queremos implementar la clase HttpUrlConnection, hay algunas cosas que también debemos tener en cuenta:

  • En el momento en que se construye la clase, la clase debe almacenar datos para futuras conexiones, pero NO para hacerlos directamente
  • Cualquier método puede ser llamado en cualquier orden.
  • El cierre de OutputStream significa que se realizó la transferencia de datos, no que la conexión debe finalizar
  • Cada api usa los métodos en un orden diferente.
  • Los encabezados HTTP no distinguen entre mayúsculas y minúsculas, los mapas de Java son sensibles a mayúsculas y minúsculas.

Rápidamente dicho, hay solo muchos escollos

En la clase que diseñé, usa indicadores booleanos para recordar si se llama al método de connect y a los métodos afterPostClosure , también tiene soporte si se llama a getInputStream() antes de que se cierre OutputStream .

Esta clase también utiliza el menor ajuste posible en las secuencias devueltas por el socket para evitar que sea realmente compleja.

public class ProxiedHttpsConnection extends HttpURLConnection { private final String proxyHost; private final int proxyPort; private static final byte[] NEWLINE = "/r/n".getBytes();//should be "ASCII7" private Socket socket; private final Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private final Map<String, List<String>> sendheaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private final Map<String, List<String>> proxyheaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private final Map<String, List<String>> proxyreturnheaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private int statusCode; private String statusLine; private boolean isDoneWriting; public ProxiedHttpsConnection(URL url, String proxyHost, int proxyPort, String username, String password) throws IOException { super(url); socket = new Socket(); this.proxyHost = proxyHost; this.proxyPort = proxyPort; String encoded = Base64.encode((username + ":" + password).getBytes()) .replace("/r/n", ""); proxyheaders.put("Proxy-Authorization", new ArrayList<>(Arrays.asList("Basic " + encoded))); } @Override public OutputStream getOutputStream() throws IOException { connect(); afterWrite(); return new FilterOutputStream(socket.getOutputStream()) { @Override public void write(byte[] b, int off, int len) throws IOException { out.write(String.valueOf(len).getBytes()); out.write(NEWLINE); out.write(b, off, len); out.write(NEWLINE); } @Override public void write(byte[] b) throws IOException { out.write(String.valueOf(b.length).getBytes()); out.write(NEWLINE); out.write(b); out.write(NEWLINE); } @Override public void write(int b) throws IOException { out.write(String.valueOf(1).getBytes()); out.write(NEWLINE); out.write(b); out.write(NEWLINE); } @Override public void close() throws IOException { afterWrite(); } }; } private boolean afterwritten = false; @Override public InputStream getInputStream() throws IOException { connect(); return socket.getInputStream(); } @Override public void setRequestMethod(String method) throws ProtocolException { this.method = method; } @Override public void setRequestProperty(String key, String value) { sendheaders.put(key, new ArrayList<>(Arrays.asList(value))); } @Override public void addRequestProperty(String key, String value) { sendheaders.computeIfAbsent(key, l -> new ArrayList<>()).add(value); } @Override public Map<String, List<String>> getHeaderFields() { return headers; } @Override public void connect() throws IOException { if (connected) { return; } connected = true; socket.setSoTimeout(getReadTimeout()); socket.connect(new InetSocketAddress(proxyHost, proxyPort), getConnectTimeout()); StringBuilder msg = new StringBuilder(); msg.append("CONNECT "); msg.append(url.getHost()); msg.append('':''); msg.append(url.getPort() == -1 ? 443 : url.getPort()); msg.append(" HTTP/1.0/r/n"); for (Map.Entry<String, List<String>> header : proxyheaders.entrySet()) { for (String l : header.getValue()) { msg.append(header.getKey()).append(": ").append(l); msg.append("/r/n"); } } msg.append("Connection: close/r/n"); msg.append("/r/n"); byte[] bytes; try { bytes = msg.toString().getBytes("ASCII7"); } catch (UnsupportedEncodingException ignored) { bytes = msg.toString().getBytes(); } socket.getOutputStream().write(bytes); socket.getOutputStream().flush(); byte reply[] = new byte[200]; byte header[] = new byte[200]; int replyLen = 0; int headerLen = 0; int newlinesSeen = 0; boolean headerDone = false; /* Done on first newline */ InputStream in = socket.getInputStream(); while (newlinesSeen < 2) { int i = in.read(); if (i < 0) { throw new IOException("Unexpected EOF from remote server"); } if (i == ''/n'') { if (newlinesSeen != 0) { String h = new String(header, 0, headerLen); String[] split = h.split(": "); if (split.length != 1) { proxyreturnheaders.computeIfAbsent(split[0], l -> new ArrayList<>()).add(split[1]); } } headerDone = true; ++newlinesSeen; headerLen = 0; } else if (i != ''/r'') { newlinesSeen = 0; if (!headerDone && replyLen < reply.length) { reply[replyLen++] = (byte) i; } else if (headerLen < reply.length) { header[headerLen++] = (byte) i; } } } String replyStr; try { replyStr = new String(reply, 0, replyLen, "ASCII7"); } catch (UnsupportedEncodingException ignored) { replyStr = new String(reply, 0, replyLen); } // Some proxies return http/1.1, some http/1.0 even we asked for 1.0 if (!replyStr.startsWith("HTTP/1.0 200") && !replyStr.startsWith("HTTP/1.1 200")) { throw new IOException("Unable to tunnel. Proxy returns /"" + replyStr + "/""); } SSLSocket s = (SSLSocket) ((SSLSocketFactory) SSLSocketFactory.getDefault()) .createSocket(socket, url.getHost(), url.getPort(), true); s.startHandshake(); socket = s; msg.setLength(0); msg.append(method); msg.append(" "); msg.append(url.toExternalForm().split(String.valueOf(url.getPort()), -2)[1]); msg.append(" HTTP/1.0/r/n"); for (Map.Entry<String, List<String>> h : sendheaders.entrySet()) { for (String l : h.getValue()) { msg.append(h.getKey()).append(": ").append(l); msg.append("/r/n"); } } if (method.equals("POST") || method.equals("PUT")) { msg.append("Transfer-Encoding: Chunked/r/n"); } msg.append("Host: ").append(url.getHost()).append("/r/n"); msg.append("Connection: close/r/n"); msg.append("/r/n"); try { bytes = msg.toString().getBytes("ASCII7"); } catch (UnsupportedEncodingException ignored) { bytes = msg.toString().getBytes(); } socket.getOutputStream().write(bytes); socket.getOutputStream().flush(); } private void afterWrite() throws IOException { if (afterwritten) { return; } afterwritten = true; socket.getOutputStream().write(String.valueOf(0).getBytes()); socket.getOutputStream().write(NEWLINE); socket.getOutputStream().write(NEWLINE); byte reply[] = new byte[200]; byte header[] = new byte[200]; int replyLen = 0; int headerLen = 0; int newlinesSeen = 0; boolean headerDone = false; /* Done on first newline */ InputStream in = socket.getInputStream(); while (newlinesSeen < 2) { int i = in.read(); if (i < 0) { throw new IOException("Unexpected EOF from remote server"); } if (i == ''/n'') { if (headerDone) { String h = new String(header, 0, headerLen); String[] split = h.split(": "); if (split.length != 1) { headers.computeIfAbsent(split[0], l -> new ArrayList<>()).add(split[1]); } } headerDone = true; ++newlinesSeen; headerLen = 0; } else if (i != ''/r'') { newlinesSeen = 0; if (!headerDone && replyLen < reply.length) { reply[replyLen++] = (byte) i; } else if (headerLen < header.length) { header[headerLen++] = (byte) i; } } } String replyStr; try { replyStr = new String(reply, 0, replyLen, "ASCII7"); } catch (UnsupportedEncodingException ignored) { replyStr = new String(reply, 0, replyLen); } /* We asked for HTTP/1.0, so we should get that back */ if ((!replyStr.startsWith("HTTP/1.0 200")) && !replyStr.startsWith("HTTP/1.1 200")) { throw new IOException("Server returns /"" + replyStr + "/""); } } @Override public void disconnect() { try { socket.close(); } catch (IOException ex) { Logger.getLogger(ProxiedHttpsConnection.class.getName()).log(Level.SEVERE, null, ex); } } @Override public boolean usingProxy() { return true; } }

Errores actuales con el código anterior:

  • Las secuencias no se cierran en los errores durante la publicación
  • Las secuencias no se cierran durante los errores con el contacto inicial con el proxy
  • No soporta redirecciones http
  • No es compatible con las cosas http 1.1 como la codificación fragmentada y gzip, pero esto no es un problema ya que nos anunciamos como un cliente http1.0.

El código anterior se puede utilizar como:

ProxiedHttpsConnection n = new ProxiedHttpsConnection( new URL("https://.com:443/questions/3304006/persistent-httpurlconnection-in-java"), "proxy.example.com", 8080, "root", "flg83yvem#"); n.setRequestMethod("GET"); n.addRequestProperty("User-Agent", "Java test https://.com/users/1542723/ferrybig"); //try (OutputStream out = n.getOutputStream()) { // out.write("Hello?".getBytes()); //} try (InputStream in = n.getInputStream()) { byte[] buff = new byte[1024]; int length; while ((length = in.read(buff)) >= 0) { System.out.write(buff, 0, length); } }

Si va a usar esto con un tipo de selector de proxy, debe verificar el protocolo de la url para ver si es http o https, si es http, no use esta clase, y en su lugar adjunte el encabezado manualmente como:

httpURLConnection.setRequestProperty("Proxy-Authorization", "Basic " + encoded);

¿Por qué no usar httpsUrlConnection.setSSLSocketFactory

Si bien java tiene este método, los intentos de usarlo le mostrarán por qué no funcionará, java simplemente sigue llamando a createSocket(Socket s, String host, int port, boolean autoClose) con una conexión ya abierta, lo que hace imposible hacerlo. las cosas proxy de forma manual.