Java 配置 HTTP/Socks 代理

我就是女王發表於2023-10-09

在網路請求過程中,使用代理是一種常見的需求。代理伺服器可以幫助我們隱藏真實的 IP 地址、加速訪問速度、訪問公司特定內網等等要求。在 Java 中,我們可以透過一些庫或框架來實現代理的設定和使用。


但如果使用 OkHttp、HttpClient 亦或是 Retrofit 和 Feign,需要實現 Socks 協議代理都需要實現SSLSocketFactory類或ConnectionSocketFactory介面的子類,重寫createSokcet方法,實現起來非常的麻煩。如果代理還需要使用者名稱密碼驗證(大部分都會有),還需要實現Authenticator的子類,並透過ThreadLocal分配到請求各自的執行緒中,整個過程需要自己寫很多程式碼,無比煩人。


而本文將介紹如何使用一種最簡單的方法,即使用宣告式 HTTP 框架 Forest,結合@HTTPProxy 和 @SocksProxy註解來傳送 HTTP/HTTPS 請求,來快速實現代理功能。


Forest 的基本使用

新增 Forest 依賴


<dependency>

    <groupId>com.dtflys.forest</groupId>

    <artifactId>forest-spring-boot-starter</artifactId>

    <version>1.5.33</version>

</dependency>

如果您的專案不是 spring-boot 專案,請看官方檔案來配置不同環境下的依賴。


先看看沒有代理的情況


// 定義一個 Forest 客戶端介面

public interface MyClient {

    // 當呼叫該方法時,會自動使用 Get 請求訪問地址

    @Get("")

    String getData();

}

假如這個地址是需要透過代理才能正常訪問,那麼以下程式碼將不會成功


// 注入 Forest 客戶端例項

@Resource

MyClient myClient;


... ...

// 網路請求將會失敗

String data = myClient.getData();

使用 HTTP 代理

在介面上掛上@HTTPProxy介面即可


// 透過 @HTTPProxy 註解配置代理服務的地址和埠

@HTTPProxy(host = "127.0.0.1", port = "1081")

public interface MyClient {

    @Get("")

    String getData();

}

如果代理服務需要驗證


// 透過 @HTTPProxy 註解配置代理服務的地址和埠以及使用者驗證資訊

@HTTPProxy(host = "127.0.0.1", port = "1081", username = "root", password = "123456")

public interface MyClient {

    @Get("")

    String getData();

}

如果您需要連的是 Socks 協議的代理埠,那也很簡單,可以用上面的方法如法炮製,只不過註解名換了一下而已


// 透過 @SocksProxy 註解配置 Socks 協議代理服務的地址和埠

@SocksProxy(host = "127.0.0.1", port = "1081")

public interface MyClient {

    @Get("")

    String getData();

}

加上使用者名稱密碼


// 透過 @SocksProxy 註解配置 Socks 協議代理服務的地址和埠以及使用者驗證資訊

@SocksProxy(host = "127.0.0.1", port = "1081", username = "root", password = "123456")

public interface MyClient {

    @Get("")

    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("")

    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("/data1")

   String getData1();

}


@MyProxy

public interface MyClient2 {

   @Get("/data2")

   String getData2();

}

此時,MyClient1 和 MyClient2 介面的請求都會走同樣的代理


非宣告式方式

以上都是以宣告式的方式,配合@HTTProxy以及@SocksProxy註解來完成 HTTP/Socks 代理的設定過程的。


如果不想定義介面、配置、註解等等玩意兒,那用程式設計式的API直接幹就完了。


// 透過 HTTP 的代理傳送請求

String data1 = Forest.get("")

      .proxy(ForestProxy.http("127.0.0.1", 1081)

                .username("root")

                .password("123456"))

      .executeAsString();

      

// 透過 Socks 的代理傳送請求

String data2 = Forest.get("")

      .proxy(ForestProxy.socks("127.0.0.1", 1081)

                .username("root")

                .password("123456"))

      .executeAsString();


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70033972/viewspace-2987658/,如需轉載,請註明出處,否則將追究法律責任。

相關文章