内网转发钉钉API

1. 问题场景

内网有台服务器A,不能访问外网,但是能通过局域网访问一台具有外网访问功能的服务器B

内网服务器需要访问钉钉API,需要需要通过外网服务器转发。

2. nginx配置转发Https

# 查看nginx版本,确认是否安装了ssl模块
/usr/local/nginx/sbin/nginx -V

nginx配置https需要ssl模块支持,所以nginx没有安装ssl模块需要重新配置

# 进入nginx安装包目录
cd /usr/local/nginx/nginx-1.13.7
# 重新配置模块
./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-stream --with-stream_ssl_module
# 安装
make
# 备份已安装的nginx
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak
# 暂停nginx
/usr/local/nginx/sbin/nginx -s stop
# 替换安装好的nginx
cp ./objs/nginx /usr/local/nginx/sbin/nginx
# 查看nginx版本,确认是否安装了ssl模块
/usr/local/nginx/sbin/nginx -V
# 切换到nginx程序目录
cd /usr/local/nginx/sbin
# 启动
nginx

生成黑证书

# 查看版本
openssl version
# 输入密码生成私钥
openssl genrsa -des3 -out server.key 2048
# 根据密码生成CSR,需要输入,组织、组织单位、国家、地区、城市和通用名称等信息
openssl req -nodes -new -key server.key -out server.csr
# 生成新证书
openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt

配置nginx转发

server {
    server_name B服务器对应的局域网ip;
    listen 11523 ssl;
    resolver 114.114.114.114 valid=60s ipv6=off;
​
    ssl_certificate /usr/local/nginx/ssl_key/server.crt;
    ssl_certificate_key /usr/local/nginx/ssl_key/server.key;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!AESGCM;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
​
    location / {
        proxy_pass https://oapi.dingtalk.com;
        proxy_read_timeout 300;
        proxy_connect_timeout 300;
        proxy_redirect off;
​
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host 'oapi.dingtalk.com';
        proxy_set_header X-Real-IP $remote_addr;
    }
    
    location /v1.0/ {
        proxy_pass https://api.dingtalk.com;
        proxy_read_timeout 300;
        proxy_connect_timeout 300;
        proxy_redirect off;
​
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host 'api.dingtalk.com';
        proxy_set_header X-Real-IP $remote_addr;
    }
}

3. java 调用https接口

// 解决javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException
// 解决PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException
// 创建一个信任所有证书的 SSLContext 对象
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        // 不验证客户端证书
    }
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        // 不验证服务器证书
    }
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }
}}, new SecureRandom());
​
// 创建一个跳过证书验证的 SSLSocketFactory 对象
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
​
// 禁用主体备用名称验证,解决SSLHandshakeException: No subject alternative names present
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        if("B服务器对应的局域网ip".equals(hostname)){
            return true;
        } else {
            return false;
        }
    }
});
​
// 发送 HTTPS 请求
URL url = new URL("https://B服务器对应的局域网ip:端口/api具体接口名");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
InputStream input = conn.getInputStream();