【问题标题】:JAX WS client cannot authenticateJAX WS 客户端无法进行身份验证
【发布时间】:2014-03-14 14:35:22
【问题描述】:

我正在尝试在标准 JAVA 7 JAX WS 工具的帮助下使用安全(HTTPS 模式)Web 服务。 此网络服务需要身份验证。

我已经成功地将证书添加到我的本地 java 密钥库。 我在 wsimport 工具的帮助下从 WSDL 文件生成了所有需要的类。

现在我正在尝试使用以下调用来运行一个简单的测试:

public class ReportingWebServiceTest {
    static ReportingServiceService service;
    static ReportingService port;

    @BeforeClass
    public static void setUpBeforeClass(){
        service = new ReportingServiceService();
        port = service.getReportingServicePort();
        Map<String, Object> rContext = ((BindingProvider) port).getRequestContext();
        Map<String, List<String>> headers = new HashMap<String, List<String>>();
        headers.put("Authorization", Collections.singletonList("Basic YWRtaW5AYWRhcHRsb2dpYy5jb206MTIxMjE****="));

//      headers.put("Username", Collections.singletonList("*****@******.com"));
//      headers.put("Password", Collections.singletonList("********"));
        rContext.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
//      rContext.put(BindingProvider.USERNAME_PROPERTY, "*****@******.com");
//      rContext.put(BindingProvider.PASSWORD_PROPERTY, "********");
    }   

    @Test
    public void test() {
        WEBCAMPAIGNROW row = port.getCampaignRowById(14081);
        toConsole(row.toString());
    }

    protected static void toConsole(String msg) {
        System.out.println(msg);
    }
}

当我运行测试时,它给了我以下异常:

javax.xml.ws.WebServiceException: Failed to access the WSDL at: https://reporting-stage.admp.mtv3.adtlgc.com/admp/ReportingService?wsdl. It failed with: 
    Got Server returned HTTP response code: 401 for URL: https://reporting-stage.admp.mtv3.adtlgc.com/admp/ReportingService?wsdl while opening stream from https://reporting-stage.admp.mtv3.adtlgc.com/admp/ReportingService?wsdl.
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex(RuntimeWSDLParser.java:173)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:155)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:120)
    at com.sun.xml.internal.ws.client.WSServiceDelegate.parseWSDL(WSServiceDelegate.java:257)
    at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:220)
    at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:168)
    at com.sun.xml.internal.ws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:96)
    at javax.xml.ws.Service.<init>(Service.java:77)
    at com.enreach.admp.reporting.ReportingServiceService.<init>(ReportingServiceService.java:42)
    at me.enreach.automation.mtv3.ReportingWebServiceTest.setUpBeforeClass(ReportingWebServiceTest.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.io.IOException: Got Server returned HTTP response code: 401 for URL: https://reporting-stage.admp.mtv3.adtlgc.com/admp/ReportingService?wsdl while opening stream from https://reporting-stage.admp.mtv3.adtlgc.com/admp/ReportingService?wsdl
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.createReader(RuntimeWSDLParser.java:842)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.resolveWSDL(RuntimeWSDLParser.java:283)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:140)
    ... 23 more
Caused by: java.io.IOException: Server returned HTTP response code: 401 for URL: https://reporting-stage.admp.mtv3.adtlgc.com/admp/ReportingService?wsdl
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1626)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
    at java.net.URL.openStream(URL.java:1037)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.createReader(RuntimeWSDLParser.java:827)
    ... 25 more

如您所见,我尝试应用三种不同的身份验证技术,但没有任何运气。例外在所有三种情况下都是相同的。 我做错了什么?

附:如果我尝试在浏览器中访问 WSDL,我在代码中使用的凭据可以正常工作。

【问题讨论】:

  • 我也尝试过 Authenticator.setDefault(.......) 方法,它也对我没有帮助。
  • 你知道需要什么样的认证吗?
  • @user1902288 不,我不确定需要哪种身份验证。但正如你所看到的,我已经尝试使用我目前找到的所有类型。
  • 你能试试这两个建议中的任何一个吗?
  • 认证必须是HTTP Basic认证。

标签: java web-services authentication jax-ws webservice-client


【解决方案1】:

您的问题与 SSL 证书无关。您的问题与身份验证有关。服务实例需要能够访问 WSDL 内容(在您的存根调用实际的 Web 方法之前)但它失败了,这就是您收到该错误的原因。

您有 2 个解决方案:

  1. 注册默认验证器:

    static {
    
        java.net.Authenticator.setDefault(new java.net.Authenticator() {
    
            @Override
            protected java.net.PasswordAuthentication getPasswordAuthentication() {
                return new java.net.PasswordAuthentication("myuser", "mypasswd".toCharArray());
            }
        });
    }
    
  2. 下载 WSDL 文档并将其保存到本地存储,然后使用本地 WSDL 文件。 对于此解决方案,您必须创建服务实例,而不是像以前那样使用生成的代码。

    Service service  = Service.create(
                        new URL(**"file:///C:/reportingService.wsdl"**), 
                        new QName("http://services.app/", "ReportingService")
                   );
    
    // ...
    
    binding.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myuser");   
    binding.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypasswd");
    

【讨论】:

  • Loc,如果我使用 wsimport 生成类和使用你在 2 中建议的方法,你能告诉我有什么区别吗?
  • 解决方案 1 使用静态初始化进行身份验证 -> 为所有生成的客户端存根共享身份验证。如果不同的客户端存根可能有不同的用户名/密码,则需要解决方案 2。
  • 好的,静态初始化对我有用。我的代码中还有一个愚蠢的错误,我在初始化 web 服务和端口之后放置了静态身份验证 sn-p。所以在我弄清楚之后一切正常。
  • 如果我使用 BindingProvider prov = (BindingProvider) port; 而不是做解决方案 1; prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "UserName"); prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "密码");它应该工作吗?
【解决方案2】:

让我们尝试一些事情:

1.) 你知道服务器需要什么样的身份验证吗?

2.) 当您尝试访问https://reporting-stage.admp.mtv3.adtlgc.com/admp/ReportingService?wsdl 时,您看到什么了吗?例如我假设有一个 IP 白名单或其他东西,你会在那里输入凭据或类似的?因为我在那里什么都看不到。

3.) 当涉及到代码时,我以这种方式通过 HTTP 对安全 Web 服务进行身份验证:

ReportingServiceService service new ReportingServiceService();
ReportingService port = service.getReportingServicePort();
BindingProvider binding = (BindingProvider) port;

// Configure service endpoint (override defined one of the WSDL) 
BindingProvider binding = (BindingProvider) port;
binding.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https..");


// Add HTTP Basic Authentification credentials to this request      
binding.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myuser");   
binding.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypasswd");
port.getCampaignRowById(14081);

将上述端点属性设置为存储在 wsdl 的 wsdl:port... 部分中的任何内容,我希望它看起来像这样(并且应该是默认值...):

 <wsdl:service name="ReportingServiceFoo">
    <wsdl:port binding="tns:ReportingServiceFoo" name="ReportingService">
        <soap:address location"https://myserver.com/ReportingService">
    </wsdl:port>
 </wsdl:service>

编辑

BindingProvider.ENDPOINT_ADDRESS_PROPERTY

用于在运行时设置目标端点。因此,如果 Web 服务的端点与 wsdl 中的端点不同,请确保以这种方式将其设置为实际端点。

【讨论】:

  • 感谢您的建议。回答您的问题:1)我不知道它需要什么样的身份验证 2)如果我在浏览器中输入该 URL,我会看到弹出窗口要求我输入用户名和密码。我输入凭据,然后我可以看到 WSDL。是的,可能有 IP 白名单,所以你看不到这个。
  • 好的。这么好,这意味着身份验证在我们的大多数服务中都有效......你有没有尝试过上面的代码“我的”(我看不出有什么不同,但让我们试试
  • 此 WSDL 元素中的地址与运行 Web 服务的实际地址不同。开发人员告诉我这应该不是问题,因为
    元素中的地址只是默认地址,而实际的 Web 服务可能部署在其他 URL。
  • 完全正确(抱歉,上面的答案有点不清楚),这就是为什么我使用 BindingProvider.ENDPOINT_ADDRESS_PROPERTY 在运行时将地址设置为实际的服务端点(以覆盖默认的 WSDL 端点)。那么,你试过了吗?
  • 请注意,我必须承认我不确定您尝试设置凭据的方式(3 个方法)与我设置凭据的方式之间是否有任何区别。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多