Java JAX-WS - 添加 SOAP 标头
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2322953/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JAX-WS - Adding SOAP Headers
提问by Jordan Allan
I am trying to create a standalone client to consume some web services. I must add my username and password to the SOAP Header. I tried adding the credentials as follows:
我正在尝试创建一个独立的客户端来使用一些 Web 服务。我必须将我的用户名和密码添加到 SOAP 标头中。我尝试添加凭据如下:
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");
...
When I call a method on the service I get the following exception:
当我在服务上调用方法时,出现以下异常:
com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5048E: One of "SOAP Header" elements required.
What am I doing wrong? How would I add these properties to the SOAP Header?
我究竟做错了什么?我如何将这些属性添加到 SOAP 标头?
Edited: I was using JAX-WS 2.1 included in JDK6. I am now using JAX-WS 2.2. I now get the following exception:
编辑:我使用的是 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.
How do I go about creating this token?
我该如何创建这个令牌?
采纳答案by Pascal Thivent
Not 100% sure as the question is missing some details but if you are using JAX-WS RI, then have a look at Adding SOAP headers when sending requests:
不是 100% 确定,因为问题缺少一些细节,但如果您使用的是 JAX-WS RI,请查看发送请求时添加 SOAP 标头:
The portable way of doing this is that you create a
SOAPHandler
and mess with SAAJ, but the RI provides a better way of doing this.When you create a proxy or dispatch object, they implement
BindingProvider
interface. When you use the JAX-WS RI, you can downcast toWSBindingProvider
which defines a few more methods provided only by the JAX-WS RI.This interface lets you set an arbitrary number of Header object, each representing a SOAP header. You can implement it on your own if you want, but most likely you'd use one of the factory methods defined on
Headers
class to create one.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) );
这样做的可移植方式是您创建一个
SOAPHandler
并使用 SAAJ,但 RI 提供了一种更好的方式来执行此操作。当您创建代理或调度对象时,它们会实现
BindingProvider
接口。当您使用 JAX-WS RI 时,您可以向下转换到WSBindingProvider
which 定义了一些仅由 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) );
Update your code accordingly and try again. And if you're not using JAX-WS RI, please update your question and provide more context information.
相应地更新您的代码并重试。如果您没有使用 JAX-WS RI,请更新您的问题并提供更多上下文信息。
Update:It appears that the web service you want to call is secured with WS-Security/UsernameTokens. This is a bit different from your initial question. Anyway, to configure your client to send usernames and passwords, I suggest to check the great post Implementing the WS-Security UsernameToken Profile for Metro-based web services(jump to step 4). Using NetBeans for this step might ease things a lot.
更新:您要调用的 Web 服务似乎使用 WS-Security/UsernameTokens 进行保护。这与您最初的问题有点不同。无论如何,要将您的客户端配置为发送用户名和密码,我建议查看为基于 Metro 的 Web 服务实现 WS-Security UsernameToken Profile的精彩文章(跳转到第 4 步)。在这一步中使用 NetBeans 可能会减轻很多事情。
回答by cristianoms
Also, if you're using Maven to build your project, you'll need to add the following dependency:
此外,如果您使用 Maven 构建项目,则需要添加以下依赖项:
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>{currentversion}/version>
</dependency>
This provides you with the class com.sun.xml.ws.developer.WSBindingProvider
.
这为您提供了类com.sun.xml.ws.developer.WSBindingProvider
。
Link: https://mvnrepository.com/artifact/com.sun.xml.ws/jaxws-rt
链接:https: //mvnrepository.com/artifact/com.sun.xml.ws/jaxws-rt
回答by Evgeny
Data can be transferred in SOAP header (JaxWS) by using @WebParam(header = true):
可以使用@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);
If you want to generate a client with SOAP Headers, you need to use -XadditionalHeaders:
如果要生成带有 SOAP Header 的客户端,则需要使用 -XadditionalHeaders:
wsimport -keep -Xnocompile -XadditionalHeaders -Xdebug http://12.34.56.78:8080/TestHeaders/somewsdl?wsdl -d /home/evgeny/DEVELOPMENT/JAVA/gen
If don't need @Oneway web service, you can use Holder:
如果不需要@Oneway web service,可以使用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);
回答by Eduardo Dennis
Use maven and the plugin jaxws-maven-plugin. this will generate a web service client. Make sure you are setting the xadditionalHeadersto true. This will generate methods with header inputs.
使用 maven 和插件jaxws-maven-plugin。这将生成一个 Web 服务客户端。确保将xadditionalHeaders设置为 true。这将生成带有标题输入的方法。
回答by edubriguenti
I'm adding this answer because none of the others worked for me.
我添加这个答案是因为其他人都没有为我工作。
I had to add a Header Handlerto the Proxy:
我有一个添加头部处理器到代理服务器:
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) {
//
}
}
In the proxy, I just add the Handler:
在代理中,我只添加了处理程序:
BindingProvider bp =(BindingProvider)basicHttpBindingAuthentication;
bp.getBinding().getHandlerChain().add(new SOAPHeaderHandler(authenticatedToken));
bp.getBinding().getHandlerChain().add(new SOAPLoggingHandler());
回答by Grigory Kislin
In jaxws-rt-2.2.10-ources.jar!\com\sun\xml\ws\transport\http\client\HttpTransportPipe.java
:
在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);
So, Map<String, List<String>>
from requestContext with key MessageContext.HTTP_REQUEST_HEADERS
will be copied to SOAP headers.
Sample of Application Authentication with JAX-WS via headers
因此,Map<String, List<String>>
带有键的 requestContextMessageContext.HTTP_REQUEST_HEADERS
将被复制到 SOAP 标头。通过标头使用 JAX-WS 进行应用程序身份验证的示例
BindingProvider.USERNAME_PROPERTY
and BindingProvider.PASSWORD_PROPERTY
keys are processed special way in HttpTransportPipe.addBasicAuth()
, adding standard basic authorization Authorization
header.
BindingProvider.USERNAME_PROPERTY
和BindingProvider.PASSWORD_PROPERTY
密钥在 中以特殊方式处理HttpTransportPipe.addBasicAuth()
,添加标准的基本授权Authorization
头。
See also Message Context in JAX-WS
另请参阅JAX-WS 中的消息上下文
回答by MahmoudPrime
you can add the username and password to the SOAP Header
您可以将用户名和密码添加到 SOAP Header
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);
回答by Peter Becker
I struggled with all the answers here, starting with Pascal's solution, which is getting harder with the Java compiler not binding against rt.jar
by default any more (and using internal classes makes it specific to that runtime implementation).
我在这里挣扎了所有答案,从Pascal 的解决方案开始,随着 Java 编译器不再rt.jar
默认绑定(并且使用内部类使其特定于该运行时实现),这变得越来越难。
The answer from edubriguentibrought me close. The way the handler is hooked up in the final bit of code didn't work for me, though - it was never called.
edubriguenti 的回答让我很接近。但是,处理程序在最后一段代码中的连接方式对我不起作用 - 它从未被调用过。
I ended up using a variation of his handler class, but wired it into the javax.xml.ws.Service
instance like this:
我最终使用了他的处理程序类的变体,但将其连接到javax.xml.ws.Service
实例中,如下所示:
Service service = Service.create(url, qname);
service.setHandlerResolver(
portInfo -> Collections.singletonList(new SOAPHeaderHandler(handlerArgs))
);
Service service = Service.create(url, qname);
service.setHandlerResolver(
portInfo -> Collections.singletonList(new SOAPHeaderHandler(handlerArgs))
);
回答by rumberomelo
The best option (for my of course) is do it yourserfl. It means you can modify programattly all parts of the SOAP message
最好的选择(当然对我来说)是自己动手。这意味着您可以以编程方式修改 SOAP 消息的所有部分
Binding binding = prov.getBinding();
List<Handler> handlerChain = binding.getHandlerChain();
handlerChain.add( new ModifyMessageHandler() );
binding.setHandlerChain( handlerChain );
And the ModifyMessageHandler source could be
而 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" );
...
...
I hope this helps you
我希望这可以帮助你