我有使用 Apache CXF 3.1.18 和 Java 1.7 的代码。它对第三方服务进行 SOAP 调用以创建票证。CXF 从服务的 WSDL 生成我的客户端存根。管理该服务的组现在想要启用“要求对所有传入 SOAP 请求进行 WS-Security 标头验证”,这还设置了“需要对传入 WSDL 请求进行授权”设置。我的工作是使我的代码与启用这些设置的服务兼容。有关通信类的列表,请参见下文。
下面的代码在禁用两个安全设置时有效,并在服务系统中生成票证。启用安全性会生成如下所示的第一个堆栈跟踪;添加身份验证器使代码更进一步,但我一直试图破译“javax.xml.ws.soap.SOAPFaultException: Invalid QName in mapping: wsse:InvalidSecurity”并继续解决我的问题。
package com.myCompany.service.webservice.client;
import java.net.Authenticator;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Holder;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import com.myCompany.instrumentation.Instrumentation;
import com.myCompany.logging.Log;
/* Note: these next three classes are generated by CXF from the WSDL */
import com.myCompany.service.ServiceTicket;
import com.myCompany.service.ServiceTicketManager;
import com.myCompany.service.ServiceTicketManagerSoap;
import com.myCompany.myProject.biz.TicketTO;
public class ServiceWebServiceClientImpl implements
ServiceWebServiceClient {
private TicketTO uTicket;
private ServiceTicket serviceTicket;
private List<TeamGroupTO> result;
private static Log log = Instrumentation.getSmLog( "ServiceClient");
private static String SERVICE_WSDL ="https://myCompanyDev.service.com/ServiceTicketManager.do?WSDL";
private static String SERVICE_USERNAME="myServiceUser";
private static String SERVICE_PASSWORD="mYsERVICEpASSWORD";
private static String SERVICE_SERVICE_NAME="ServiceTicketManager";
private static String SERVICE_URL="http://www.service.com";
static{
try {
SERVICE_WSDL = /* get from configuration */;
SERVICE_USERNAME=/* get from configuration */;
SERVICE_PASSWORD=/* get from configuration */;
SERVICE_SERVICE_NAME=/* get from configuration */;
SERVICE_URL=/* get from configuration */;
} catch (Exception e) {
log.warn( e.toString(), e);
}
}
// Main entry point here
@Override
public TicketTO createTicket( TicketTO ticket) {
uTicket = ticket;
ServiceTicketManagerSoap port = getPort();
ServiceTicket serviceTicket =
createServiceTicket( ticket);
Holder<ServiceTicket> serviceTicket_holder =
new Holder<ServiceTicket>( serviceTicket);
/* This is Line 2, that throws the Invalid QName exception */
port.createTicket(serviceTicket_holder);
serviceTicket = serviceTicket_holder.value;
uTicket.setSvcTicketId(serviceTicket.getId());
uTicket.setSvcTicketNumber(serviceTicket.getNumber());
return uTicket;
}
private static ServiceTicketManagerSoap getPort() {
/* Code Block 1 location */
URL wsdlURL = ServiceTicketManager.WSDL_LOCATION;
try {
wsdlURL = new URL(ServiceWebServiceClientImpl.SERVICE_WSDL);
} catch (MalformedURLException e) {
log.warn( e.toString(), e);
}
QName qname = new QName(ServiceWebServiceClientImpl.SERVICE_URL,
ServiceWebServiceClientImpl.SERVICE_SERVICE_NAME);
/* This is Line 1, that throws the 401 Exception */
ServiceTicketManager ss = new ServiceTicketManager( wsdlURL, qname);
ServiceTicketManagerSoap port = ss.getServiceTicketManagerSoap();
Client client = ClientProxy.getClient( port);
HTTPConduit http = (HTTPConduit) client.getConduit();
http.getAuthorization().setUserName( ServiceWebServiceClientImpl.SERVICE_USERNAME);
http.getAuthorization().setPassword( ServiceWebServiceClientImpl.SERVICE_PASSWORD);
BindingProvider portBP = (BindingProvider) port;
Map<String, Object> requestContext = portBP.getRequestContext();
requestContext.put( BindingProvider.USERNAME_PROPERTY, SERVICE_USERNAME);
requestContext.put( BindingProvider.PASSWORD_PROPERTY, SERVICE_PASSWORD);
return port;
}
private ServiceTicket createServiceTicket( TicketTO ticket)
{
// TicketTOOld.EnumUserTypes.Reporter
ServiceTicket serviceTicket =
new ServiceTicket();
// ...
return serviceTicket;
}
}
我在使用 CXF 使用具有 HTTP Basic 身份验证的 Web 服务时发现此页面401 错误,这建议我添加以下代码:
Authenticator.setDefault( new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
ServiceWebServiceClientImpl.SERVICE_USERNAME,
ServiceWebServiceClientImpl.SERVICE_PASSWORD.toCharArray());
}
});
在下面记为 /* 代码块 1 位置 */ 的位置。
通过此添加,代码通过了第 1 行,但在第 2 行中指出了“映射中的 QName 无效”异常。请参阅堆栈跟踪 #2。
我还发现了这个页面 How to add Basic Authorization to a wsdl on startup with cxf? 但我不确定我应该如何将给定的解决方案应用到我的代码中。对于 CXF 如何将在该页面上进行的 API 调用转换为我上面给出的代码正在执行的操作,我真的没有足够的“全局”知识。
到目前为止,我的搜索技能还不能胜任这项任务。我个人的目标是更多地了解 CXF 的架构以及 java.net.Authenticator 类如何融入整个画面。但是我一直在寻找这一切如何工作的架构,而不是各个类的手册页。
这段代码是由一些有才华的承包商通过相当快地搜索问题而组合在一起并开始工作的,但我不知道是否应该或可以简化它。
我已经下载了 WSDL,并用它来生成客户端存根。有没有办法告诉代码使用 WSDL 的本地副本而不是下载它?
堆栈跟踪 #1:
javax.xml.ws.WebServiceException: org.apache.cxf.service.factory.ServiceConstructionException: Failed to create service.
at org.apache.cxf.jaxws.ServiceImpl.initialize(ServiceImpl.java:162)
at org.apache.cxf.jaxws.ServiceImpl.<init>(ServiceImpl.java:129)
at org.apache.cxf.jaxws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:82)
at javax.xml.ws.Service.<init>(Service.java:77)
at com.myCompany.myProject.service.ServiceTicketManager.<init>(ServiceTicketManager.java:43)
at com.myCompany.myProject.service.webservice.client.ServiceWebServiceClientImpl.getPort(ServiceWebServiceClientImpl.java:109)
at com.myCompany.myProject.service.webservice.client.ServiceWebServiceClientImpl.createIncidentTicket(ServiceWebServiceClientImpl.java:77)
at com.myCompany.myProject.webstar.biz.CreateIncidentManager_Test.createIncident(CreateIncidentManager_Test.java:79)
...
Caused by: org.apache.cxf.service.factory.ServiceConstructionException: Failed to create service.
at org.apache.cxf.wsdl11.WSDLServiceFactory.<init>(WSDLServiceFactory.java:87)
at org.apache.cxf.jaxws.ServiceImpl.initializePorts(ServiceImpl.java:217)
at org.apache.cxf.jaxws.ServiceImpl.initialize(ServiceImpl.java:160)
... 34 more
Caused by: javax.wsdl.WSDLException: WSDLException: faultCode=PARSER_ERROR: Problem parsing 'https://mycompanyinstance.service.com/ServiceTicketManager.do?WSDL'.: java.io.IOException: Server returned HTTP response code: 401 for URL: https://mycompanyinstance.service.com/ServiceTicketManager.do?WSDL
at com.ibm.wsdl.xml.WSDLReaderImpl.getDocument(WSDLReaderImpl.java:2198)
at com.ibm.wsdl.xml.WSDLReaderImpl.readWSDL(WSDLReaderImpl.java:2390)
at com.ibm.wsdl.xml.WSDLReaderImpl.readWSDL(WSDLReaderImpl.java:2422)
at org.apache.cxf.wsdl11.WSDLManagerImpl.loadDefinition(WSDLManagerImpl.java:290)
at org.apache.cxf.wsdl11.WSDLManagerImpl.getDefinition(WSDLManagerImpl.java:181)
at org.apache.cxf.wsdl11.WSDLServiceFactory.<init>(WSDLServiceFactory.java:85)
... 36 more
Caused by: java.io.IOException: Server returned HTTP response code: 401 for URL: https://mycompanyinstance.service.com/ServiceTicketManager.do?WSDL
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1694)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:263)
at org.apache.xerces.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
at org.apache.xerces.impl.XMLVersionDetector.determineDocVersion(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
at com.ibm.wsdl.xml.WSDLReaderImpl.getDocument(WSDLReaderImpl.java:2188)
... 41 more
堆栈跟踪 #2:
javax.xml.ws.soap.SOAPFaultException: Invalid QName in mapping: wsse:InvalidSecurity
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:161)
at com.sun.proxy.$Proxy38.createTicket(Unknown Source)
at com.myCompany.myProject.service.webservice.client.ServiceWebServiceClientImpl.createTicketTicket(ServiceWebServiceClientImpl.java:nn (port.createTicket, Line 2 noted in the code)
at com.myCompany.myProject.webstar.biz.CreateTicketManager_Test.createTicket(CreateTicketManager_Test.java:73)
...
Caused by: java.lang.RuntimeException: Invalid QName in mapping: wsse:InvalidSecurity
at org.apache.cxf.staxutils.StaxUtils.readQName(StaxUtils.java:1863)
at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.unmarshalFault(Soap11FaultInInterceptor.java:65)
at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:52)
at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:41)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:309)
at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:112)
at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:69)
at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:34)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:309)
at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:825)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1689)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1565)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1366)
at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:663)
at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:63)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:309)
at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:523)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:432)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:347)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:305)
at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:139)
... 30 more