杰瑞科技汇

Java HTTP代理如何配置与使用?

Of course! Here’s a comprehensive guide to creating and using an HTTP proxy in Java, covering different approaches from simple system properties to building a custom proxy server.

We'll cover three main scenarios:

  1. Configuring an existing Java application to use a proxy.
  2. Making a single HTTP request through a proxy.
  3. Building a simple, custom HTTP proxy server from scratch.

Configuring an Application to Use a Proxy (Client-Side)

This is the most common scenario. You have a Java application (e.g., a web scraper, a client calling an API) and you need to route its traffic through a corporate proxy or a public proxy server.

Method A: Using System Properties (Global)

This is the simplest way and affects most of Java's built-in HTTP clients, including java.net.HttpURLConnection and the legacy Apache HttpClient 3.x.

You set these properties before your application makes any network calls.

public class SystemPropertyProxyExample {
    public static void main(String[] args) {
        // --- Proxy Configuration ---
        String proxyHost = "proxy.yourcompany.com";
        int proxyPort = 8080;
        String proxyUser = "user";
        String proxyPassword = "password";
        // --- Set System Properties ---
        System.setProperty("http.proxyHost", proxyHost);
        System.setProperty("http.proxyPort", String.valueOf(proxyPort));
        System.setProperty("https.proxyHost", proxyHost); // Often, the same proxy is used for HTTPS
        System.setProperty("https.proxyPort", String.valueOf(proxyPort));
        // --- For Proxy Authentication (NTLM, Basic, Digest) ---
        // This is for the Authenticator, which is a more modern approach.
        // System properties alone are often not enough for auth.
        // See Method B for the correct way to handle auth.
        // --- Test the connection ---
        try {
            URL url = new URL("http://httpbin.org/ip");
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            int responseCode = connection.getResponseCode();
            System.out.println("Response Code: " + responseCode);
            if (responseCode == HttpURLConnection.HTTP_OK) {
                try (BufferedReader in = new BufferedReader(
                        new InputStreamReader(connection.getInputStream()))) {
                    String inputLine;
                    StringBuilder response = new StringBuilder();
                    while ((inputLine = in.readLine()) != null) {
                        response.append(inputLine);
                    }
                    System.out.println("Response: " + response.toString());
                }
            } else {
                System.out.println("GET request failed.");
            }
        } catch (IOException e) {
            System.err.println("Failed to connect through proxy: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

Important Note on Authentication: System properties like http.proxyUser and http.proxyPassword exist but are not reliably supported across all Java versions and environments. The recommended way to handle proxy authentication is with an Authenticator (see Method B).

Method B: Using java.net.Authenticator (Recommended for Auth)

The Authenticator class is the standard, modern way to handle authentication for both proxies and servers. It's more flexible and reliable than system properties for auth.

import java.io.IOException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.net.HttpURLConnection;
public class AuthenticatorProxyExample {
    public static void main(String[] args) {
        // --- Proxy Configuration ---
        String proxyHost = "proxy.yourcompany.com";
        int proxyPort = 8080;
        String proxyUser = "user";
        String proxyPassword = "password";
        // --- Set System Properties for Host and Port ---
        System.setProperty("http.proxyHost", proxyHost);
        System.setProperty("http.proxyPort", String.valueOf(proxyPort));
        System.setProperty("https.proxyHost", proxyHost);
        System.setProperty("https.proxyPort", String.valueOf(proxyPort));
        // --- Set the Authenticator for Proxy Authentication ---
        Authenticator.setDefault(new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                // This method is called for both proxy and server auth.
                // We check the requestor type to ensure it's for a proxy.
                if (getRequestorType() == RequestorType.PROXY) {
                    return new PasswordAuthentication(proxyUser, proxyPassword.toCharArray());
                }
                return null; // No credentials for server-side requests
            }
        });
        // --- Test the connection (same as before) ---
        try {
            URL url = new URL("http://httpbin.org/ip");
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            // ... rest of the connection code is the same ...
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Method C: Using Apache HttpClient 4.x (Modern & Recommended)

For any serious HTTP client work, the Apache HttpClient 4.x library is the industry standard. It gives you fine-grained control over proxy settings.

First, add the dependency to your pom.xml:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.14</version> <!-- Use the latest version -->
</dependency>
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class HttpClientProxyExample {
    public static void main(String[] args) throws Exception {
        // --- Proxy Configuration ---
        HttpHost proxy = new HttpHost("proxy.yourcompany.com", 8080);
        // --- Credentials Provider for Proxy Auth ---
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(
                new AuthScope(proxy.getHostName(), proxy.getPort()),
                new UsernamePasswordCredentials("user", "password"));
        // --- Build the HttpClient with Proxy Configuration ---
        try (CloseableHttpClient httpClient = HttpClients.custom()
                .setProxy(proxy)
                .setDefaultCredentialsProvider(credsProvider)
                .build()) {
            // --- Execute the request ---
            HttpGet request = new HttpGet("http://httpbin.org/ip");
            System.out.println("Executing request " + request.getRequestLine() + " via " + proxy);
            try (CloseableHttpResponse response = httpClient.execute(request)) {
                System.out.println("Response status: " + response.getStatusLine());
                String responseBody = EntityUtils.toString(response.getEntity());
                System.out.println("Response body: " + responseBody);
            }
        }
    }
}

Building a Custom HTTP Proxy Server

This is an advanced topic but incredibly useful for debugging, security analysis, or creating custom routing logic. A simple proxy works as a "man-in-the-middle."

How it works:

  1. The client connects to our proxy server.
  2. The proxy server reads the client's request (which contains the real destination URL, e.g., GET http://example.com/).
  3. The proxy server opens a new connection to the real destination (example.com).
  4. The proxy server forwards the client's request to the destination.
  5. The destination server sends the response back to our proxy server.
  6. The proxy server forwards the response back to the original client.

Here is a simple, single-threaded implementation using Java's built-in NIO (Selector) for non-blocking I/O.

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
public class SimpleHttpProxyServer {
    private static final int BUFFER_SIZE = 8192;
    private static final int PROXY_PORT = 8888;
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(PROXY_PORT));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("Proxy server started on port " + PROXY_PORT);
        ByteBuffer clientBuffer = ByteBuffer.allocate(BUFFER_SIZE);
        ByteBuffer serverBuffer = ByteBuffer.allocate(BUFFER_SIZE);
        while (true) {
            selector.select();
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iter = selectedKeys.iterator();
            while (iter.hasNext()) {
                SelectionKey key = iter.next();
                if (key.isAcceptable()) {
                    acceptNewClient(selector, serverSocketChannel);
                }
                if (key.isReadable()) {
                    handleReadableKey(key, clientBuffer, serverBuffer, selector);
                }
                iter.remove();
            }
        }
    }
    private static void acceptNewClient(Selector selector, ServerSocketChannel serverSocketChannel) throws IOException {
        SocketChannel clientChannel = serverSocketChannel.accept();
        clientChannel.configureBlocking(false);
        System.out.println("Accepted new client connection: " + clientChannel.getRemoteAddress());
        clientChannel.register(selector, SelectionKey.OP_READ, new ConnectionState());
    }
    private static void handleReadableKey(SelectionKey key, ByteBuffer clientBuffer, ByteBuffer serverBuffer, Selector selector) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        ConnectionState state = (ConnectionState) key.attachment();
        ByteBuffer buffer = (channel == state.clientChannel) ? clientBuffer : serverBuffer;
        int bytesRead = channel.read(buffer);
        if (bytesRead == -1) {
            // Connection closed by one side
            closeConnection(key, state);
            return;
        }
        buffer.flip();
        if (channel == state.clientChannel && state.serverChannel == null) {
            // This is the first read from the client, need to parse the request
            String requestLine = readLine(buffer);
            if (requestLine == null) return; // Not enough data yet
            System.out.println("Received request: " + requestLine);
            String[] parts = requestLine.split(" ");
            if (parts.length < 3) {
                closeConnection(key, state);
                return;
            }
            String[] hostPort = parts[1].substring(7).split(":"); // Remove "http://"
            String host = hostPort[0];
            int port = (hostPort.length > 1) ? Integer.parseInt(hostPort[1]) : 80;
            // Connect to the target server
            SocketChannel serverChannel = SocketChannel.open();
            serverChannel.connect(new InetSocketAddress(host, port));
            serverChannel.configureBlocking(false);
            serverChannel.register(selector, SelectionKey.OP_READ, state);
            state.serverChannel = serverChannel;
            state.host = host;
        }
        // Forward data
        if (channel == state.clientChannel && state.serverChannel != null) {
            state.serverChannel.write(buffer);
        } else if (channel == state.serverChannel && state.clientChannel != null) {
            state.clientChannel.write(buffer);
        }
        buffer.compact(); // Prepare buffer for next read
    }
    private static String readLine(ByteBuffer buffer) {
        StringBuilder sb = new StringBuilder();
        while (buffer.hasRemaining()) {
            byte b = buffer.get();
            if (b == '\r') {
                if (buffer.hasRemaining() && buffer.get() == '\n') {
                    return sb.toString();
                }
            } else {
                sb.append((char) b);
            }
        }
        return null; // Line not complete
    }
    private static void closeConnection(SelectionKey key, ConnectionState state) throws IOException {
        System.out.println("Closing connection to " + state.host);
        if (state.clientChannel != null) state.clientChannel.close();
        if (state.serverChannel != null) state.serverChannel.close();
        key.cancel();
    }
    static class ConnectionState {
        SocketChannel clientChannel;
        SocketChannel serverChannel;
        String host;
    }
}

How to run this proxy:

  1. Save the code as SimpleHttpProxyServer.java.
  2. Compile and run it: javac SimpleHttpProxyServer.java && java SimpleHttpProxyServer
  3. Configure another application (like your web browser or a Java client) to use a proxy on localhost:8888.
  4. All HTTP traffic will be logged by your proxy server.

Limitations of this simple proxy:

  • Single-threaded: It can only handle one request at a time.
  • No HTTPS: It cannot decrypt HTTPS traffic (TLS/SSL). To handle HTTPS, the proxy would need to perform a TLS Man-in-the-Middle (MITM) attack, which involves generating its own certificates and having the client trust them. This is much more complex.
  • No persistent connections: It doesn't handle Connection: keep-alive headers correctly.

Summary and Best Practices

Scenario Tool / Method Pros Cons When to Use
Client Application System Properties Very easy to set up. Not reliable for auth, global effect. Quick scripts, simple setups where auth isn't needed.
Client Application Authenticator Standard, reliable for auth, handles proxy/server auth correctly. Still global effect, tied to java.net stack. The standard way for core Java applications (HttpURLConnection).
Client Application Apache HttpClient 4.x Industry standard, highly configurable, object-oriented, explicit proxy setup. Requires an external library. Recommended for almost all Java HTTP client work.
Server (Debugging) Custom Proxy Server Full control, can inspect/modify traffic, great for learning. Complex to implement correctly (especially HTTPS), not for production. Debugging network issues, security analysis, simple routing needs.
分享:
扫描分享到社交APP
上一篇
下一篇