到 Web 服务的 SOAP 消息 - HTTP 响应代码:URL 的 403

Posted

技术标签:

【中文标题】到 Web 服务的 SOAP 消息 - HTTP 响应代码:URL 的 403【英文标题】:SOAP message to webservice - HTTP response code: 403 for URL 【发布时间】:2018-01-06 16:21:44 【问题描述】:

我尝试将XML 文件中的SOAP 消息发送到网络服务,然后获取二进制输出并对其进行解码。 Endpoint 使用HTTPS 协议,所以我在代码中使用TrustManager 以避免PKIX 问题。你可以在这里看到我的代码:

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.X509Certificate;

public class Main
    public static void sendSoapRequest() throws Exception 
        String SOAPUrl = "URL HERE";
        String xmlFile2Send = ".\\src\\request.xml";
        String responseFileName = ".\\src\\response.xml";
        String inputLine;

        TrustManager[] trustAllCerts = new TrustManager[]  new X509TrustManager() 
            public java.security.cert.X509Certificate[] getAcceptedIssuers()  return null; 
            public void checkClientTrusted(X509Certificate[] certs, String authType)  
            public void checkServerTrusted(X509Certificate[] certs, String authType)  

         ;

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // Create all-trusting host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() 
            public boolean verify(String hostname, SSLSession session)  return true; 
        ;
        // Install the all-trusting host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

        // Create the connection with http
        URL url = new URL(SOAPUrl);
        URLConnection connection = url.openConnection();
        HttpURLConnection httpConn = (HttpURLConnection) connection;
        FileInputStream fin = new FileInputStream(xmlFile2Send);
        ByteArrayOutputStream bout = new ByteArrayOutputStream();

        copy(fin, bout);
        fin.close();

        byte[] b = bout.toByteArray();
        StringBuffer buf=new StringBuffer();
        String s=new String(b);

        b=s.getBytes();

        // Set the appropriate HTTP parameters.
        httpConn.setRequestProperty("Content-Length", String.valueOf(b.length));
        httpConn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
        httpConn.setRequestProperty("SOAPAction", "");
        httpConn.setRequestMethod("POST");
        httpConn.setDoOutput(true);

        OutputStream out = httpConn.getOutputStream();
        out.write(b);
        out.close();

        // Read the response.
        httpConn.connect();
        System.out.println("http connection status :"+ httpConn.getResponseMessage());
        InputStreamReader isr = new InputStreamReader(httpConn.getInputStream());
        BufferedReader in = new BufferedReader(isr);

        while ((inputLine = in.readLine()) != null)
            System.out.println(inputLine);
        FileOutputStream fos=new FileOutputStream(responseFileName);
        copy(httpConn.getInputStream(),fos);
        in.close();
    

    public static void copy(InputStream in, OutputStream out) throws IOException 

        synchronized (in) 
            synchronized (out) 
                byte[] buffer = new byte[256];
                while (true) 
                    int bytesRead = in.read(buffer);
                    if (bytesRead == -1)
                        break;
                    out.write(buffer, 0, bytesRead);
                
            
        
    

    public static void main(String args[]) throws Exception 
        sendSoapRequest();
    

执行此操作时出现以下错误代码。

线程“main”java.io.IOException 中的异常:服务器返回 HTTP 响应代码:URL 403

【问题讨论】:

您应该提供凭据以获得访问权限。用于基本身份验证的 base64 中带有登录名/密码的身份验证标头,最简单的变体,但它取决于服务器端。您应该提供所需的服务器。 @user1516873 : 无需在此服务器上进行身份验证。 403-forbidden 表示请求已经到达服务器并且是有效的,但是服务器拒绝访问所请求的资源。总而言之,SSL 连接正常,因此您调用了错误的端点或 SOAP 标头中缺少凭据。 @pedrofb :我明白了,但是当我在带有 SoapUI 的 SOAP 请求中使用 XML 文件的内容时,我会从同一个端点得到响应。 如果内容相同,则检查并比较实际发送 SOAPUI 的标头和您的连接。有可能是服务器检测到了一些不正确,将其视为 403 【参考方案1】:

你的实现没问题,实际上问题与你的Content-Type 标头有关。

text/xml; charset=utf-8 是SOAP 1.1 的默认Content-Type,它可能不是您的版本。 SOAP 1.2 expects a header of type application/soap+xml; charset=utf-8,因此将您的代码行更改为下面的代码会使其正常工作:

httpConn.setRequestProperty("Content-Type", "application/soap+xml; charset=utf-8");

在 SoapUI 中,可以检查调用请求的标头并转到窗口底部的 Headers 选项卡:

然后,您可以比较您的应用程序配置和 SoapUI 配置之间的差异。

【讨论】:

我在 SoapUI 中找不到任何标题,我也得到与此 Content-Type 完全相同的错误。 标头在响应端,而不是在请求中。提出请求并在那里检查。 响应端是application/timestamp-reply 您可以共享端点吗?或者至少是 GitHub 的服务项目?很难猜测您的实施的具体情况。 抱歉,只能从本地网络访问。【参考方案2】:

403 错误可能与您发送到服务器的soap 请求标头有关。 所有有效的主机都将允许您的 Java 应用程序信任 URL 的 SSL 证书。 检查您的服务器是否需要带有用户名/密码的soap标头。如果您有权访问此服务器,则可以通过 Web 服务器日志检查您的请求失败的位置。错误代码指向缺少 Soap 标头,尤其是带有用户名和密码的 Soap 标头

【讨论】:

不需要用户名/密码,当前SoapUI请求中没有设置。【参考方案3】:

想知道您的 SOAP 请求是否在 SAML 等标头中包含任何类型的身份验证信息。一种选择是,在上面的代码中,您读取文件并将数据发送到服务器,而不是将其发送到服务器,而是将其转储到另一个文件。转储该字节输出流。然后从该文件复制文本并将其放入 SOAP UI 并尝试运行它。那样有用吗?

【讨论】:

为什么制作另一个内容相同的文件更好? 特别是在您的 SOAP 请求包含 SAML 或其他一些二进制数据的情况下。那是看它是否在文件 IO 期间被损坏。您的代码包含以下一小部分:byte[] b = bout.toByteArray(); StringBuffer buf=new StringBuffer(); String s=new String(b); b=s.getBytes();,您将在其中将数据转换为字符串并返回字节。是否有特定的需要这样做? 正如@pedrofb 所建议的,我之前从代码中删除了这些行。 哦,好的。您是否能够捕获从 SOAP UI 发送的标头与从您的代码发送的标头。那些也一样。 是的,我向 Requestbin 发送了请求,但正文为空。我的 Java 代码有问题吗?【参考方案4】:

在我们之前遇到过类似的情况,只要尝试TrustManager 没有按预期工作,我们设法通过将证书从服务器安装到 JVM 的密钥库(JVM 用于运行应用)。您可以在几篇文章中找到有关如何操作的更多信息,例如 How to import a .cer certificate into a java keystore?

我知道这是一种强制 JVM 接受 SSL 证书的尝试,而且这个功能在应用程序上下文中会更好,但只要我们构建一个在特定应用程序服务器中运行的 Web 应用程序,实现的解决方案已被接受。

【讨论】:

以上是关于到 Web 服务的 SOAP 消息 - HTTP 响应代码:URL 的 403的主要内容,如果未能解决你的问题,请参考以下文章

wininet api 将文件发送到 http web 服务

如何将 WS-Addressing 字段添加到 SOAP 消息

发送接收SOAP消息

在调用Web服务时,是否有一种简单的方法可以获取请求的soap消息和响应的soap消息?

如何解析从文件加载的soap消息?

HTTP 标头或 SOAP 标头中的 WCF 肥皂操作?