我正在尝试创建一个独立的客户端来使用一些Web服务。我必须将我的用户名和密码添加到SOAP Header中。我尝试添加凭据如下:
OTSWebSvcsService service = new OTSWebSvcsService();
OTSWebSvcs port = service.getOTSWebSvcs();
BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");
...
当我在服务上调用方法时,我得到以下异常:
com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5048E: One of "SOAP Header" elements required.
我做错了什么?我如何将这些属性添加到SOAP标头?
编辑:我使用的是JDK6中包含的JAX-WS 2.1。我现在正在使用JAX-WS 2.2。我现在得到以下例外:
com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5509E: A security token whose type is [http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken] is required.
如何创建此令牌?
答案 0 :(得分:31)
抱歉我的英语不好。可以使用@WebParam(header = true)在SOAP头(JaxWS)中传输数据,如下所示:
@WebMethod(operationName = "SendRequest", action = "http://abcd.ru/")
@Oneway
public void sendRequest(
@WebParam(name = "Message", targetNamespace = "http://abcd.ru/", partName = "Message")
Data message,
@WebParam(name = "ServiceHeader", targetNamespace = "http://abcd.ru/", header = true, partName = "ServiceHeader")
Header serviceHeader);
如果您想要使用SOAP Headers生成客户端,则需要使用-XadditionalHeaders,如:
wsimport -keep -Xnocompile -XadditionalHeaders -Xdebug http://12.34.56.78:8080/TestHeaders/somewsdl?wsdl -d /home/evgeny/DEVELOPMENT/JAVA/gen
如果您不需要@Oneway网络服务,可以像这样使用Holder:
@WebMethod(operationName = "SendRequest", action = "http://abcd.ru/")
public void sendRequest(
@WebParam(name = "Message", targetNamespace = "http://abcd.ru/", partName = "Message")
Data message,
@WebParam(name = "ServiceHeader", targetNamespace = "http://abcd.ru/", header = true, partName = "ServiceHeader")
Holder<Header> serviceHeader);
答案 1 :(得分:27)
不是100%肯定,因为问题缺少一些细节,但如果您使用的是JAX-WS RI,那么请查看Adding SOAP headers when sending requests:
这样做的便携方式就是这样 你创造了一个
SOAPHandler
和混乱 与SAAJ,但RI提供了 更好的方法。创建代理或分派时 对象,他们实现
BindingProvider
界面。当你 使用JAX-WS RI,你可以向下转换WSBindingProvider
定义了一个 更少的方法只提供了 JAX-WS RI。此界面可让您设置 任意数量的Header对象, 每个代表一个SOAP标头。您 如果你可以自己实现它 想要,但很可能你会使用其中一个 定义的工厂方法
Headers
类来创建一个。import com.sun.xml.ws.developer.WSBindingProvider; HelloPort port = helloService.getHelloPort(); // or something like that... WSBindingProvider bp = (WSBindingProvider)port; bp.setOutboundHeader( // simple string value as a header, like <simpleHeader>stringValue</simpleHeader> Headers.create(new QName("simpleHeader"),"stringValue"), // create a header from JAXB object Headers.create(jaxbContext,myJaxbObject) );
相应地更新您的代码,然后重试。如果您没有使用JAX-WS RI,请更新您的问题并提供更多上下文信息。
更新:您希望调用的Web服务似乎受WS-Security / UsernameTokens保护。这与您最初的问题略有不同。无论如何,要配置您的客户端发送用户名和密码,我建议检查好帖子Implementing the WS-Security UsernameToken Profile for Metro-based web services(跳转到第4步)。在此步骤中使用NetBeans可能会减轻很多事情。
答案 2 :(得分:7)
另外,如果您使用Maven构建项目,则需要添加以下依赖项:
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>{currentversion}/version>
</dependency>
这为您提供了课程com.sun.xml.ws.developer.WSBindingProvider
。
链接:https://mvnrepository.com/artifact/com.sun.xml.ws/jaxws-rt
答案 3 :(得分:2)
使用maven和插件jaxws-maven-plugin。这将生成一个Web服务客户端。确保将xadditionalHeaders设置为true。这将生成带有标头输入的方法。
答案 4 :(得分:1)
我添加了这个答案,因为其他人都没有为我工作。
我必须在代理中添加标头处理程序:
import java.util.Set;
import java.util.TreeSet;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class SOAPHeaderHandler implements SOAPHandler<SOAPMessageContext> {
private final String authenticatedToken;
public SOAPHeaderHandler(String authenticatedToken) {
this.authenticatedToken = authenticatedToken;
}
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty =
(Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
try {
SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
SOAPFactory factory = SOAPFactory.newInstance();
String prefix = "urn";
String uri = "urn:xxxx";
SOAPElement securityElem =
factory.createElement("Element", prefix, uri);
SOAPElement tokenElem =
factory.createElement("Element2", prefix, uri);
tokenElem.addTextNode(authenticatedToken);
securityElem.addChildElement(tokenElem);
SOAPHeader header = envelope.addHeader();
header.addChildElement(securityElem);
} catch (Exception e) {
e.printStackTrace();
}
} else {
// inbound
}
return true;
}
public Set<QName> getHeaders() {
return new TreeSet();
}
public boolean handleFault(SOAPMessageContext context) {
return false;
}
public void close(MessageContext context) {
//
}
}
在代理中,我只添加处理程序:
BindingProvider bp =(BindingProvider)basicHttpBindingAuthentication;
bp.getBinding().getHandlerChain().add(new SOAPHeaderHandler(authenticatedToken));
bp.getBinding().getHandlerChain().add(new SOAPLoggingHandler());
答案 5 :(得分:1)
您可以将用户名和密码添加到SOAP标头
BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"your end point"));
Map<String, List<String>> headers = new HashMap<String, List<String>>();
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");
prov.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, headers);
答案 6 :(得分:0)
在jaxws-rt-2.2.10-ources.jar!\com\sun\xml\ws\transport\http\client\HttpTransportPipe.java
:
public Packet process(Packet request) {
Map<String, List<String>> userHeaders = (Map<String, List<String>>) request.invocationProperties.get(MessageContext.HTTP_REQUEST_HEADERS);
if (userHeaders != null) {
reqHeaders.putAll(userHeaders);
因此,带有键Map<String, List<String>>
的requestContext中的MessageContext.HTTP_REQUEST_HEADERS
将被复制到SOAP头。
Application Authentication with JAX-WS via headers
BindingProvider.USERNAME_PROPERTY
和BindingProvider.PASSWORD_PROPERTY
密钥在HttpTransportPipe.addBasicAuth()
中以特殊方式处理,添加标准基本授权Authorization
标题。
答案 7 :(得分:0)
我从Pascal's solution开始努力解决所有答案,这变得越来越困难,因为Java编译器默认不再与rt.jar
绑定(并且使用内部类使其特定于该运行时)实施)。
The answer from edubriguenti带我接近。但是,处理程序在最后一段代码中的连接方式对我不起作用-从未调用过。
我最终使用了他的处理程序类的变体,但将其连接到javax.xml.ws.Service
实例中,如下所示:
Service service = Service.create(url, qname);
service.setHandlerResolver(
portInfo -> Collections.singletonList(new SOAPHeaderHandler(handlerArgs))
);
答案 8 :(得分:0)
最好的选择(当然,对我而言)是yourserfl。这意味着您可以以编程方式修改SOAP消息的所有部分
Binding binding = prov.getBinding();
List<Handler> handlerChain = binding.getHandlerChain();
handlerChain.add( new ModifyMessageHandler() );
binding.setHandlerChain( handlerChain );
而ModifyMessageHandler源可能是
@Override
public boolean handleMessage( SOAPMessageContext context )
{
SOAPMessage msg = context.getMessage();
try
{
SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
SOAPHeader header = envelope.addHeader();
SOAPElement ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
ele.addTextNode( "value_of_header" );
ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
ele.addTextNode( "value_of_header" );
ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
ele.addTextNode( "value_of_header" );
...
希望对您有帮助
答案 9 :(得分:0)
在标头中添加一个对象,我们使用此处使用的示例,但我将完成
ObjectFactory objectFactory = new ObjectFactory();
CabeceraCR cabeceraCR =objectFactory.createCabeceraCR();
cabeceraCR.setUsuario("xxxxx");
cabeceraCR.setClave("xxxxx");
使用对象工厂,我们创建了要求传递标题的对象。添加到标题中的
WSBindingProvider bp = (WSBindingProvider)wsXXXXXXSoap;
bp.setOutboundHeaders(
// Sets a simple string value as a header
Headers.create(jaxbContext,objectFactory.createCabeceraCR(cabeceraCR))
);
我们使用WSBindingProvider添加标头。如果直接使用该对象会出现一些错误,所以我们使用方法
objectFactory.createCabeceraCR(cabeceraCR)
此方法将在对象Factory上创建类似的JAXBElement
@XmlElementDecl(namespace = "http://www.creditreport.ec/", name = "CabeceraCR")
public JAXBElement<CabeceraCR> createCabeceraCR(CabeceraCR value) {
return new JAXBElement<CabeceraCR>(_CabeceraCR_QNAME, CabeceraCR.class, null, value);
}
我们获得的jaxbContext是这样的:
jaxbContext = (JAXBRIContext) JAXBContext.newInstance(CabeceraCR.class.getPackage().getName());
这会将对象添加到标题中。