文章

Java 程序全局代理设置

Java Archive

在 Java 命令行中,可以使用 -D 参数设置系统属性。这个参数可以用来设置任何系统属性,包括自定义的属性,这里我们只关注网络相关的系统属性

只需要在启动 jar 程序时使用这些参数,就能让原本不支持代理设置的 jar 程序挂上代理了。

设置 HTTP 代理示例:

1
Java -Dhttp.proxyHost=127.0.0.1 -Dhttp.proxyPort=8080 -Dhttps.proxyHost=127.0.0.1 -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts= -jar .\example.jar

注:在 powershell 中需要将参数使用 '" 包裹起来,如:Java '-Dhttp.proxyHost=127.0.0.1' '-Dhttp.proxyPort=8080' -jar .\example.jar

设置 SOCKS5 代理示例:

1
java -DsocksProxyHost=127.0.0.1 -DsocksProxyPort=1080 -jar example.jar

网络相关部分系统属性:

属性简述
socksProxyHostsocks 代理服务器的主机名
socksProxyPortsocks 代理服务器的端口号
http.proxyHosthttp 代理服务器的主机名
http.proxyPorthttp 代理服务器的端口号
http.proxyUser用于在 http 代理服务器进行认证的用户
http.proxyPassword用于在 http 代理服务器进行认证的密码
http.nonProxyHosts指定直连 ip,不走 http/https 代理

Java Code

设置全局代理:System.setProperty(K,V)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.*;

public class ProxyUtils {
    public static void main(String[] args) throws Exception {
        Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7897));
        System.out.println(verifyProxy(proxy));
    }

    /**
     * 验证 HTTP/SOCKS 代理
     * @param proxy 输入代理设置(java.net.Proxy 对象)
     *              Proxy proxy = new Proxy(java.net.Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", 7897));
     * @return 返回通过 SOCKS 服务器代理后的公网 IP
     */
    public static String verifyProxy(Proxy proxy) throws Exception {
        URL url = new URL("https://myip.ipip.net");  // Simple API to get your public IP
        URLConnection connection = proxy == null ? url.openConnection() : url.openConnection(proxy);
        try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
            return in.readLine();
        }
    }

    /**
     * 验证全局 HTTP/SOCKS 代理,无需传入参数
     * @return 返回通过 SOCKS 服务器代理后的公网 IP
     */
    public static String verifyGlobalProxy() throws Exception {
        URL url = new URL("https://myip.ipip.net");  // Simple API to get your public IP
        URLConnection connection = url.openConnection();
        try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
            return in.readLine();
        }
    }

    public static void setupGlobalSocksProxy(String host, Integer port) throws Exception {
        setupGlobalSocksProxy(host,port , null,null );
    }

    /**
     * 通过 System.setProperty(K,V) 设置网络代理相关系统属性,来设置 SOCKS 代理。
     * 注:该方式存在弊端,可能会因为外部系统代理的优先级高于内部程序的设置,而导致程序中的代理设置失效。
     * 代码参考:https://stackoverflow.com/questions/120797/how-do-i-set-the-proxy-to-be-used-by-the-jvm
     * @param host SOCKS 代理服务器地址
     * @param port SOCKS 代理服务器端口
     * @param username 认证用户
     * @param password 认证密码
     */
    public static void setupGlobalSocksProxy(String host, Integer port, String username, String password) {
        // Set the global SOCKS proxy settings
        System.setProperty("socksProxyHost", host);
        System.setProperty("socksProxyPort", port.toString());

        // If authentication is required
        if (username != null && password != null) {
            System.setProperty("java.net.socks.username", username);
            System.setProperty("java.net.socks.password", password);
            Authenticator.setDefault(new ProxyAuth(username, password));
        }
    }


    public static void setupGlobalHttpProxy(String host, Integer port) throws Exception {
        setupGlobalHttpProxy(host,port , null,null );
    }

    /**
     * 通过 System.setProperty(K,V) 设置网络代理相关系统属性,来设置 HTTP/HTTPS 代理。
     * 注:该方式存在弊端,可能会因为外部系统代理的优先级高于内部程序的设置,而导致程序中的代理设置失效。
     * 代码参考:https://stackoverflow.com/questions/120797/how-do-i-set-the-proxy-to-be-used-by-the-jvm
     * @param host HTTP/HTTPS 代理服务器地址
     * @param port HTTP/HTTPS 代理服务器端口
     * @param username 认证用户
     * @param password 认证密码
     */
    public static void setupGlobalHttpProxy(String host, Integer port, String username, String password) {
        System.setProperty("http.proxyHost", host);
        System.setProperty("http.proxyPort", port.toString());
        System.setProperty("https.proxyHost", host);
        System.setProperty("https.proxyPort", port.toString());

        if (username != null && password != null) {
            System.setProperty("http.proxyUser", username);
            System.setProperty("http.proxyPassword", password);
            System.setProperty("https.proxyUser", username);
            System.setProperty("https.proxyPassword", password);
            System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
            System.setProperty("jdk.https.auth.tunneling.disabledSchemes", "");
            Authenticator.setDefault(new ProxyAuth(username, password));
        }
    }


    /**
     * 通过 System.clearProperty(K,V) 清除网络代理相关系统属性。
     * 注:可清除外部系统代理设置效果。
     * 建议先使用该方法清除外部系统代理设置后,再使用 setupGlobalSocksProxy(host, port) 方法设置全局代理,这样能确保程序中的代理设置生效。
     */
    public static void unsetSystemProxy() throws Exception {
        System.clearProperty("http.proxyHost");
        System.clearProperty("http.proxyPort");
        System.clearProperty("https.proxyHost");
        System.clearProperty("https.proxyPort");
        System.clearProperty("socksProxyHost");
        System.clearProperty("socksProxyPort");
    }

    static class ProxyAuth extends Authenticator {
        private PasswordAuthentication auth;

        private ProxyAuth(String username, String password) {
            auth = new PasswordAuthentication(username, password == null ? new char[]{} : password.toCharArray());
        }

        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return auth;
        }
    }
}

设置全局代理:ProxySelector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import java.io.IOException;
import java.net.*;
import java.util.Collections;
import java.util.List;

import static xxx.ProxyUtils.verifyGlobalProxy;

public class CustomProxySelector extends ProxySelector {

    public static void main(String[] args) throws Exception {
        // 设置自定义的 ProxySelector
        ProxySelector.setDefault(new CustomProxySelector("127.0.0.1", 7897, ProxyType.HTTP));

        // 设置全局的 Authenticator
        Authenticator.setDefault(new ProxyAuthenticator("admin", "123456"));

        // 请求 https://myip.ipip.net 验证代理出口地址
        System.out.println(verifyGlobalProxy());

        // 清除 ProxySelector
        ProxySelector.setDefault(null);
        System.out.println(verifyGlobalProxy());
    }

    // 枚举定义代理类型
    public enum ProxyType {
        HTTP, SOCKS
    }

    private final Proxy proxy;

    public CustomProxySelector(String proxyHost, int proxyPort, ProxyType proxyType) {
        if (proxyType == ProxyType.HTTP) {
            // HTTP 代理
            this.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
        } else if (proxyType == ProxyType.SOCKS) {
            // SOCKS 代理
            this.proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(proxyHost, proxyPort));
        } else {
            // 不使用代理
            this.proxy = Proxy.NO_PROXY;
        }
    }

    @Override
    public List<Proxy> select(URI uri) {
        return Collections.singletonList(proxy);
    }

    @Override
    public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
        System.err.println("Connection to " + uri + " failed: " + ioe.getMessage());
    }

    static class ProxyAuthenticator extends Authenticator {
        private final String username;
        private final String password;

        public ProxyAuthenticator(String username, String password) {
            this.username = username;
            this.password = password;
        }

        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(username, password.toCharArray());
        }
    }
}

参考文章

本文由作者按照 CC BY 4.0 进行授权

© h0ny. 保留部分权利。

本站由 Jekyll 生成,采用 Chirpy 主题。