Java 配置 HTTP/Socks 代理竟能如此简单

查看 113|回复 9
作者:DtFlys   
在网络请求过程中,使用代理是一种常见的需求。代理服务器可以帮助我们隐藏真实的 IP 地址、加速访问速度、访问公司特定内网等等要求。在 Java 中,我们可以通过一些库或框架来实现代理的设置和使用。
但如果使用 OkHttp 、HttpClient 亦或是 Retrofit 和 Feign ,需要实现 Socks 协议代理都需要实现SSLSocketFactory类或ConnectionSocketFactory接口的子类,重写createSokcet方法,实现起来非常的麻烦。如果代理还需要用户名密码验证(大部分都会有),还需要实现Authenticator的子类,并通过ThreadLocal分配到请求各自的线程中,整个过程需要自己写很多代码,无比烦人。
而本文将介绍如何使用一种最简单的方法,即使用声明式 HTTP 框架 Forest ,结合@HTTPProxy 和 @SocksProxy注解来发送 HTTP/HTTPS 请求,来快速实现代理功能。
Forest 的基本使用
添加 Forest 依赖
    com.dtflys.forest
    forest-spring-boot-starter
    1.5.33
如果您的项目不是 spring-boot 项目,请看官方文档来配置不同环境下的依赖。
先看看没有代理的情况
// 定义一个 Forest 客户端接口
public interface MyClient {
    // 当调用该方法时,会自动使用 Get 请求访问地址 https://example.com
    @Get("https://example.com")
    String getData();
}
假如https://example.com这个地址是需要通过代理才能正常访问,那么以下代码将不会成功
// 注入 Forest 客户端实例
@Resource
MyClient myClient;
... ...
// 网络请求将会失败
String data = myClient.getData();
使用 HTTP 代理
在接口上挂上@HTTPProxy接口即可
// 通过 @HTTPProxy 注解配置代理服务的地址和端口
@HTTPProxy(host = "127.0.0.1", port = "1081")
public interface MyClient {
    @Get("https://example.com")
    String getData();
}
如果代理服务需要验证
// 通过 @HTTPProxy 注解配置代理服务的地址和端口以及用户验证信息
@HTTPProxy(host = "127.0.0.1", port = "1081", username = "root", password = "123456")
public interface MyClient {
    @Get("https://example.com")
    String getData();
}
使用 Socks 代理
如果您需要连的是 Socks 协议的代理端口,那也很简单,可以用上面的方法如法炮制,只不过注解名换了一下而已
// 通过 @SocksProxy 注解配置 Socks 协议代理服务的地址和端口
@SocksProxy(host = "127.0.0.1", port = "1081")
public interface MyClient {
    @Get("https://example.com")
    String getData();
}
加上用户名密码
// 通过 @SocksProxy 注解配置 Socks 协议代理服务的地址和端口以及用户验证信息
@SocksProxy(host = "127.0.0.1", port = "1081", username = "root", password = "123456")
public interface MyClient {
    @Get("https://example.com")
    String getData();
}
全局配置
如果不想把代理的参数( host, port 等)写死在注解代码中,可以通过字符串模板来引用配置文件的属性
先在application.yml配置文件中添加以下配置(属性名可以自己随意起):
proxy:
   host: 127.0.0.1
   port: 1081
   username: root
   password: 123456
通过字符串模板在注解中进行引用
@SocksProxy(
    host = "#{proxy.host}",
    port = "#{proxy.port}",
    username = "#{proxy.username}",
    password = "#{proxy.password}"
)
public interface MyClient {
    @Get("https://example.com")
    String getData();
}
封装注解
如果您有很多接口类要配置代理,并且不想在每个接口上放这么一大坨参数,可以使用自定义注解对@HTTPProxy或@SocksProxy进行封装
// 自定义一个注解 @MyProxy
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
// 将 @SockProxy 注解以及参数添加到这里
@SocksProxy(
    host = "#{proxy.host}",
    port = "#{proxy.port}",
    username = "#{proxy.username}",
    password = "#{proxy.password}"
)
public @interface MyProxy {
}
然后在需要代理的接口上挂上您自定义的@MyProxy注解就可以了
@MyProxy
public interface MyClient1 {
   @Get("https://example.com/data1")
   String getData1();
}
@MyProxy
public interface MyClient2 {
   @Get("https://example.com/data2")
   String getData2();
}
此时,MyClient1 和 MyClient2 接口的请求都会走同样的代理
非声明式方式
以上都是以声明式的方式,配合@HTTProxy以及@SocksProxy注解来完成 HTTP/Socks 代理的设置过程的。
如果不想定义接口、配置、注解等等玩意儿,那用编程式的 API 直接干就完了。
// 通过 HTTP 的代理发送请求
String data1 = Forest.get("https://example.com")
      .proxy(ForestProxy.http("127.0.0.1", 1081)
                .username("root")
                .password("123456"))
      .executeAsString();
      
// 通过 Socks 的代理发送请求
String data2 = Forest.get("https://example.com")
      .proxy(ForestProxy.socks("127.0.0.1", 1081)
                .username("root")
                .password("123456"))
      .executeAsString();

代理, string, 注解, Java

xiaooloong   
你说得对,但是 word 说全文有 1067 个字
《非常简单》
NessajCN   
python:
import requests
resp = requests.get('http://go.to',
proxies=dict( http='socks5://user:pass@host:port',
https='socks5://user:pass@host:port'))
「如此简单」
DtFlys
OP
  
@xiaooloong 如果你认真看完文章,对比过其它 Java 的解决方案,就知道为什么它如此简单了
DtFlys
OP
  
@NessajCN Python 确实简单,但 Java 里很难找到简单的方案
aosan926   
之前的项目中有用过这个框架,后来在升级 Spring Boot 3 的时候不支持就切到 Retrofit ,感觉也挺好用的
mmdsun   
感谢分享,框架看起来不错。
https://www.baeldung.com/spring-6-http-interface
Java 用新版本 SOCKS 代理会很简单:
HttpClient.create().proxy(proxyOptions -> proxyOptions.type(ProxyProvider.Proxy.SOCKS5)
.host(..)
.port(..)
);
如果用 URLConnection con = url.openConnection();可以用启动参数,这种是 HTTP 代理。
java -Dhttp.proxyHost=127.0.0.1 -Dhttp.proxyPort=3128
DtFlys
OP
  
@mmdsun 感谢分享,Java11 以后的 http 确实要比以前好很多
DtFlys
OP
  
@aosan926 Forest 1.5.30 版本开始支持 springboot3 了,但 maven 坐标了
mmdsun   
@DtFlys 这实是 reactor.netty.http.client 的包,spring webflux 项目会引入这个包,配合 spring 的 WebClien 使用。不过,目前仅支持反应式的项目,使用人还比较少。
@Bean
public WebClient webClient(){
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector( httpClient))
}
您需要登录后才可以回帖 登录 | 立即注册

返回顶部