在Springboot项目部署时遇到,centos服务器上,curl请求目标地址不通 ,curl -x 可以请求通的解决办法

发布于:2025-06-24 ⋅ 阅读:(18) ⋅ 点赞:(0)

在甲方服务器部署项目时,通常遇到需要开通外网权限的问题,有的是直接给开通服务器的白名单,就可以直接访问白名单外网地址了。也有的是通过网络转发,将url前面的部分替换,可以进行网络请求。有一次遇到一个罕见的,对方应是使用squid进行代理的。直接curl外网地址是不通的,使用curl -x 代理服务器ip:端口 目标地址可以访问通。针对此种场景,测试了以下配置代方法

1. 全局环境变量配置

在centos中配置当前用户或者全局环境变量,是可行的,配置完成后,curl命令后不用-x就可以直接访问了。但是这种方法,本服务器上部署的java和nginx确是无效的

# 编辑配置文件
 vim ~/.bashrc # 或 ~/.bash_profile 
# 添加以下内容(替换为您的代理服务器信息) 
export http_proxy=http://10.10.10.61:8080 
export https_proxy=http://10.10.10.61:8080 
# 设置 NO_PROXY 变量,指定哪些域名不需要通过代理(逗号分隔,支持通配符) 
export no_proxy="localhost,127.0.0.1,192.168.1.0/24,.example.com" 
# 使配置生效 
source ~/.bashrc

2. springboot web服务内,httpClient配置

在使用1中的方法,配置环境变量后,发现使用Springboot服务请求时,还是不通。提示域名无法解析。

使用了网上的方法,在启动jar包时,配置如下的启动参数或者环境变量依然无效。

java -Dhttp.proxyHost=10.20.102.61 -Dhttp.proxyPort=8080 -Dhttps.proxyHost=10.20.102.61 -Dhttps.proxyPort=8080  -Dsun.net.spi.nameservice.provider.1=dns,sun -Djava.net.preferIPv4Stack=true -jar

最后使用了在代码里统一配置httpClient的方式实现

依赖

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>
  • HttpProxyClientUtil.java
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Apache HttpClient 4.x 实现的 HTTP 请求工具类(支持代理、SSL 绕过、多种请求体)
 */
public class HttpProxyClientUtil {

    // -------------------- 基础配置 --------------------
    // 连接超时(毫秒)
    private static final int CONNECT_TIMEOUT = 5000;
    // 读取超时(毫秒)
    private static final int READ_TIMEOUT = 5000;

    // -------------------- 代理配置 --------------------
    // 代理主机(需替换为实际代理 IP/域名)
    private static final String PROXY_HOST = "10.10.10.61";
    // 代理端口(需替换为实际代理端口)
    private static final int PROXY_PORT = 8080;


    // -------------------- 构建 HttpClient(支持代理、SSL 绕过) --------------------
    public static CloseableHttpClient createHttpClient(boolean ignoreSsl) {
        try {
            // 1. 构建 SSL 上下文(可选:绕过证书校验)
            SSLContext sslContext = new SSLContextBuilder()
                    .loadTrustMaterial(null, (chain, authType) -> true) // 信任所有证书
                    .build();
            SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
                    sslContext,
                    NoopHostnameVerifier.INSTANCE // 跳过主机名校验
            );

            // 2. 构建请求配置(含代理)
            RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
                    .setConnectTimeout(CONNECT_TIMEOUT)
                    .setSocketTimeout(READ_TIMEOUT)
                    .setProxy(new org.apache.http.HttpHost(PROXY_HOST, PROXY_PORT)); // 设置代理

            // 3. 构建 HttpClient
            CloseableHttpClient httpClient = HttpClients.custom()
                    .setSSLSocketFactory(ignoreSsl? sslSocketFactory : SSLConnectionSocketFactory.getSystemSocketFactory())
                    .setDefaultRequestConfig(requestConfigBuilder.build())
                    .build();

            return httpClient;
        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
            throw new RuntimeException("构建 HttpClient 失败", e);
        }
    }

    // -------------------- GET 请求 --------------------
    /**
     * 发送 GET 请求(支持代理、SSL 绕过)
     * @param url       请求地址
     * @param ignoreSsl 是否忽略 SSL 证书校验(生产环境慎用)
     * @return 响应内容(字符串)
     */
    public static String doGet(String url, boolean ignoreSsl) {
        CloseableHttpClient httpClient = createHttpClient(ignoreSsl);
        HttpGet httpGet = new HttpGet(url);

        // 可在此处添加请求头(示例)
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return EntityUtils.toString(entity, StandardCharsets.UTF_8);
            }
            return "";
        } catch (IOException e) {
            throw new RuntimeException("GET 请求失败: " + url, e);
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // -------------------- POST 表单请求 --------------------
    /**
     * 发送 POST 表单请求(application/x-www-form-urlencoded)
     * @param url       请求地址
     * @param params    表单参数(key-value)
     * @param ignoreSsl 是否忽略 SSL 证书校验(生产环境慎用)
     * @return 响应内容(字符串)
     */
    public static String doPostForm(String url, Map<String, String> params, boolean ignoreSsl) {
        CloseableHttpClient httpClient = createHttpClient(ignoreSsl);
        HttpPost httpPost = new HttpPost(url);

        // 构建表单参数
        List<NameValuePair> formParams = new ArrayList<>();
        params.forEach((k, v) -> formParams.add(new BasicNameValuePair(k, v)));
        httpPost.setEntity(new UrlEncodedFormEntity(formParams, StandardCharsets.UTF_8));

        // 设置请求头(表单默认 Content-Type)
        httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return EntityUtils.toString(entity, StandardCharsets.UTF_8);
            }
            return "";
        } catch (IOException e) {
            throw new RuntimeException("POST 表单请求失败: " + url, e);
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // -------------------- POST JSON 请求 --------------------
    /**
     * 发送 POST JSON 请求(application/json)
     * @param url       请求地址
     * @param jsonBody  JSON 字符串
     * @param ignoreSsl 是否忽略 SSL 证书校验(生产环境慎用)
     * @return 响应内容(字符串)
     */
    public static String doPostJson(String url, String jsonBody, boolean ignoreSsl) {
        CloseableHttpClient httpClient = createHttpClient(ignoreSsl);
        HttpPost httpPost = new HttpPost(url);

        // 设置请求头(JSON 场景)
        httpPost.setHeader("Content-Type", "application/json");
        httpPost.setEntity(new org.apache.http.entity.StringEntity(jsonBody, StandardCharsets.UTF_8));

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return EntityUtils.toString(entity, StandardCharsets.UTF_8);
            }
            return "";
        } catch (IOException e) {
            throw new RuntimeException("POST JSON 请求失败: " + url, e);
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // -------------------- 测试示例(main 方法) --------------------
    public static void main(String[] args) {
        // 1. GET 请求示例(带代理、忽略 SSL 校验)
        String getUrl = "https://www.example.com/api/get";
        String getResponse = doGet(getUrl, true);
        System.out.println("GET 响应: " + getResponse);

    }
}

  • HttpClientConfig.java
package com.test.demo.config;

import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HttpClientConfig {

    @Bean
    public CloseableHttpClient httpClient() {
        // 这里可复用上面的 createHttpClient 逻辑,或直接构建带代理的 HttpClient
        return HttpProxyClientUtil.createHttpClient(true);
    }
}
  • ProxyHttpService.java
package com.test.demo.demos.web;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class ProxyHttpService {

    private final HttpClient httpClient;

    public ProxyHttpService(HttpClient httpClient) {
        this.httpClient = httpClient;
    }

    /**
     * 发送GET请求
     */
    public String sendGetRequest(String url) throws IOException, ParseException {
        HttpGet httpGet = new HttpGet(url);
        try (CloseableHttpResponse response = (CloseableHttpResponse)httpClient.execute(httpGet)) {
            return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("请求失败", e);
        }
    }

    /**
     * 发送POST请求
     */
    public String sendPostRequest(String url, Map<String, String> params) throws IOException, ParseException {
        HttpPost httpPost = new HttpPost(url);

        // 设置POST参数
        if (params != null && !params.isEmpty()) {
            List<NameValuePair> formParams = new ArrayList<>();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            httpPost.setEntity(new UrlEncodedFormEntity(formParams));
        }

        try (CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost)) {
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity);
        }
    }
}
  • ProxyHttpController.java
package com.test.demo.demos.web;


import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/proxy/api/http")
public class ProxyHttpController {

    private final ProxyHttpService proxyHttpService;

    public ProxyHttpController(ProxyHttpService proxyHttpService) {
        this.proxyHttpService = proxyHttpService;
    }

    /**
     * 通过代理发送GET请求
     */
    @GetMapping("/get")
    public String sendGet() {
        try {
            return proxyHttpService.sendGetRequest("https://api.test.com.cn/sys/getCaptchaBase64");
        } catch (Exception e) {
            e.printStackTrace();
            return "Error: " + e.getMessage();
        }
    }

    /**
     * 通过代理发送POST请求
     */
    @PostMapping("/post")
    public String sendPost(@RequestParam("url") String url,
                           @RequestBody(required = false) Map<String, String> params) {
        try {
            return proxyHttpService.sendPostRequest(url, params);
        } catch (Exception e) {
            return "Error: " + e.getMessage();
        }
    }
}

3. SpringGateWay配置转发

方法2,对原项目代理改动还是比较大的,如果你使用的不是httpclient的请求方式,基于gateway批量转发,也是一个不错的选择。

  • 依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor.netty</groupId>
            <artifactId>reactor-netty-http</artifactId>
        </dependency>
  • GatewayProxyConfig.java
package com.example.gateway.demos.web;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway.config.HttpClientCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.ProxyProvider;

import javax.net.ssl.SSLException;
import java.util.Arrays;

@Configuration
public class GatewayProxyConfig {

    @Bean
    public HttpClientCustomizer proxyCustomizer(ProxyProperties proxyProperties) {
        return httpClient -> {
                // 使用最新的 ProxyProvider API
                return httpClient.proxy(proxy -> {
                    ProxyProvider.Builder builder = proxy
                            .type(ProxyProvider.Proxy.HTTP)
                            .host(proxyProperties.getHost())
                            .port(proxyProperties.getPort());

                    // 如果需要代理认证
                    if (proxyProperties.getUsername() != null) {
                        builder.username(proxyProperties.getUsername())
                                .password(s -> proxyProperties.getPassword());
                    }

//                    // 设置无需代理的主机列表
//                    if (proxyProperties.getNonProxyHosts() != null) {
//                        String[] nonProxyHosts = proxyProperties.getNonProxyHosts()
//                                .split(",");
//                        builder.nonProxyHosts(Arrays.toString(nonProxyHosts));
//                    }
                });
        };
    }
    @Bean
    public HttpClientCustomizer sslCustomizer() {
        return httpClient -> {

                // 创建信任所有证书的 SSLContext(测试环境)
                // 生产环境建议使用合法证书或自定义 TrustManager
                return httpClient.secure(spec -> {
                            try {
                                spec
                                        .sslContext(buildInsecureSslContext());
                            } catch (SSLException e) {
                                e.printStackTrace();
                                throw new RuntimeException(e);
                            }
                        }
                );

        };
    }


    private io.netty.handler.ssl.SslContext buildInsecureSslContext() throws SSLException {
        return io.netty.handler.ssl.SslContextBuilder.forClient()
                .trustManager(io.netty.handler.ssl.util.InsecureTrustManagerFactory.INSTANCE)
                .build();
    }
    @Bean
    @ConfigurationProperties(prefix = "spring.cloud.gateway.httpclient.proxy")
    public ProxyProperties proxyProperties() {
        return new ProxyProperties();
    }

    // 代理配置属性类
    public static class ProxyProperties {
        private String host;
        private int port;
        private String username;
        private String password;
        private String nonProxyHosts;

        public String getHost() { return host; }
        public void setHost(String host) { this.host = host; }
        public int getPort() { return port; }
        public void setPort(int port) { this.port = port; }
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
        public String getPassword() { return password; }
        public void setPassword(String password) { this.password = password; }
        public String getNonProxyHosts() { return nonProxyHosts; }
        public void setNonProxyHosts(String nonProxyHosts) { this.nonProxyHosts = nonProxyHosts; }
    }
}
  • yml配置
# 应用服务 WEB 访问端口
server:
  port: 7777

# application.yml
spring:
  cloud:
    gateway:
      httpclient:
        pool:
          max-connections: 500    # 最大连接数
          acquire-timeout: 45000  # 获取连接超时时间(毫秒)
        proxy:
          host: 10.10.10.61
          port: 8080
          # 如果需要认证
          # username: username
          # password: password
          # 非代理主机列表
          non-proxy-hosts: "localhost,127.0.0.1,*.local"
      routes:
        # 路由 ID,唯一标识
        - id: api2
          # 匹配的路径,所有以 /api/ 开头的请求都会被路由
          uri: https://api.api2.com
          predicates:
            - Path=/api2/**
          # 重写路径,去除 /api 前缀
          filters:
            - RewritePath=/api2/(?<segment>.*), /$\{segment}
        # 路由 ID,唯一标识
        - id: api1
          # 匹配的路径,所有以 /api/ 开头的请求都会被路由
          uri: https://api1.com.cn
          predicates:
            - Path=/api1/**
          # 重写路径,去除 /api 前缀
          filters:
            - RewritePath=/api1/(?<segment>.*), /$\{segment}

4. Nginx配置转发

nginx配置这块,测试了很多方法,也没有非常有效的,最后放弃了


网站公告

今日签到

点亮在社区的每一天
去签到