Adding custom HTTP headers to Axis 1.4 web service responses - java

I'm trying to add custom HTTP headers to Axis 1.4 web servers.
I've created a handler which extends BasicHandler:
public class HttpHeaderHandler extends BasicHandler {
.
.
.
#Override
public void invoke(org.apache.axis.MessageContext arg0) throws AxisFault {
LOG.trace("invoke called");
Hashtable ht = (Hashtable)ctx.getProperty(HTTPConstants.RESPONSE_HEADERS);
if(ht == null) {
ht = new Hashtable();
}
ht.put("custom-header", "Hello");
ctx.setProperty(HTTPConstants.RESPONSE_HEADERS, ht);
}
.
.
.
}
I've added the following to server-config.wsdd:
.
.
.
<transport name="http">
<requestFlow>
<handler type="URLMapper" />
<handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler" />
</requestFlow>
<responseFlow>
<handler type="java:com.my.package.HttpHeaderHandler" />
</responseFlow>
</transport>
.
.
.
I can see that the invoke method is being called as the logging is appearing in the log file but the custom header is not being added to the response.
Any suggestions appreciated.

I was able to do this on a org.apache.axis.Stub instance by doing the following:
private Stub setHeaders(Stub stub, Hashtable<String, String> headers){
stub._setProperty(HTTPConstants.REQUEST_HEADERS, headers);
return stub;
}
Note that it is REQUIRED that the value argument to _setProperty() be a java.util.Hashtable (it gets cast later on by Axis when the Stub is used)

I added apikey for request header thanks for #romeara answer here . And it works.
Axis 1.4 sending client request from java.
YourStub stub = new YourStub();
Hashtable<String, String> headers = new Hashtable<String, String>();
headers.put("apikey", "xxxxxxxxxxxxxxxxxxxx");
stub._setProperty(HTTPConstants.REQUEST_HEADERS, headers);

I remember using the stub files generated to add HTTP user and password, check this link and locate the code that says:
_call.setProperty(org.apache.axis.client.Call.SEND_TYPE_ATTR, Boolean.FALSE);
http://www.coderanch.com/t/225102/Web-Services/java/Axis-username-password-auth-stubs
That kind of modification works.

This is what we have done
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
/**
* This method is to be used for secure SOAP calls.
* Method created as Axis 1.4 strips the security header which compiling the Java classes.
* #param username
* #param password
* #return SOAP Header
* #throws SOAPException
*/
public static SOAPHeaderElement createCustomSOAPHeader(String username, String password) throws SOAPException {
SOAPHeaderElement oHeaderElement;
SOAPElement oElement;
//Header
oHeaderElement = new SOAPHeaderElement("http://siebel.com/webservices", "Security");
oHeaderElement.setPrefix("web");
oHeaderElement.setMustUnderstand(false);
//Elements for the Header
oElement = oHeaderElement.addChildElement("UsernameToken");
oElement.addTextNode(username);
oElement = oHeaderElement.addChildElement("PasswordText");
oElement.addTextNode(password);
oElement = oHeaderElement.addChildElement("SessionType");
oElement.addTextNode("None");
return oHeaderElement;
}
Hope this helps.

Related

How to use termVector in solrJ

Solr/SolrJ Version: 6.0.0
I've set termvector component in solrconfig.xml, and the request handler is "/tvrh", I test it in the browser and this works. Now I want to use it in solrJ, but it only returns the document. The following is my code:
SolrClient solrClient = new HttpSolrClient("http://localhost:8983/solr/test");
SolrQuery solrQuery = new SolrQuery();
solrQuery.setQuery(String.format("id:%s","clueweb12-0000tw-06-17744"));
solrQuery.setRequestHandler("/tvrh");
solrQuery.set("tv", true);
solrQuery.set("tv.all", true);
QueryResponse response = solrClient.query(solrQuery);
SolrDocumentList docs = response.getResults();
for (SolrDocument doc: docs){
for (String key: doc.keySet()){
System.out.println(key);
System.out.println(doc.getFieldValue(key));
}
}
Your question is how to use a non standard request handler in solr.
Be aware that the Term Vector Component belongs to a "non standard" request handler and is not supported from solrj:
https://cwiki.apache.org/confluence/display/solr/The+Term+Vector+Component#TheTermVectorComponent-SolrJandtheTermVectorComponent
You can call "/tvrh" via solrj in a generic mode. You can not use the method SolrClient#query(SolrParams params) for this, because in this case the "request handler" is only send as "qt"-Parameter and will not be part of the url-path (and qt-Parameter is ignored by default).
So please try the method "SolrClient#request" instead.
As #Karsten R says, we could not use SolrClient.query to send request. After I searched a lot and experimented a lot, the following code could work.
SolrClient solrClient = new HttpSolrClient("http://localhost:8983/solr/trecB13");
SolrQuery solrQuery = new SolrQuery();
solrQuery.setQuery(String.format("id:%s","clueweb12-0000tw-06-17744"));
solrQuery.setRequestHandler("/tvrh");
solrQuery.set("tv", true);
solrQuery.set("tv.all", true);
try {
NamedList<Object> response = solrClient.request(new QueryRequest(solrQuery));
TermVectorExtractor extractor = new TermVectorExtractor(response);
System.out.println(extractor.getTermVectorInfo());
} catch (Exception e) {
e.printStackTrace();
}
TermVectorExtractor.java reference Sakai-Solr Github code, the function of the class is to parse resonse object and get term info. A little different from original code. The different has been shown below.
import org.apache.solr.common.util.NamedList;
import java.util.*;
public class TermVectorExtractor {
private static final String TERM_VECTORS = "termVectors";
private Map<String, Map<String, Map<String, TermInfo>>> termVectorInfo = Collections.emptyMap();
/**
* Creates a TermVectorExtractor for the given query response sent by Solr.
*
* #param queryResponse response sent by the solr server for a search query.
*/
#SuppressWarnings("unchecked")
public TermVectorExtractor(NamedList<Object> queryResponse) {
NamedList<Object> res = (NamedList<Object>) queryResponse.get(TERM_VECTORS);
if (res != null)
termVectorInfo = extractTermVectorInfo(res);
}
}

Create SAML response based on SAML request

I have developed a Java web application, and I want to implement SAML. These are the steps I believe is right to implement SAML.
The Service Provider(SP, my application in this case) sends a SAML authentication request to IdP.
The IdP then validates it and create a SAML response assertion and signs it with the certificate, and send back to SP.
SP then validates it with public key of certificate in keystore, and proceeds further based on that.
I have got a sample code and I am able to create SAML request and its like this
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="_c7b796f4-bc16-4fcc-8c1d-36befffc39c2" Version="2.0"
IssueInstant="2014-10-30T11:21:08Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
AssertionConsumerServiceURL="http://localhost:8080/mywebapp/consume.jsp">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://localhost:8080/mywebapp
</saml:Issuer>
<samlp:NameIDPolicy
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified"
AllowCreate="true"></samlp:NameIDPolicy>
<samlp:RequestedAuthnContext Comparison="exact">
<saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>
</samlp:AuthnRequest>
I can encode it and send to IdP.
I want to create sample Java code to get this SAML request and then create a SAML response.
How can I decode the request and validate it and create response? And Do I need to sign the saml response with certificate? and then send back to the SP?
Thanks.
Although this is an old post, I am adding sample code and references which I found useful.
SAMLResponse = hreq.getParameter("SAMLResponse");
InputSource inputSource = new InputSource(new StringReader(SAMLResponse));
SAMLReader samlReader = new SAMLReader();
response2 = org.opensaml.saml2.core.Response)samlReader.readFromFile(inputSource);
Now Validate the digital signature :
org.opensaml.saml2.core.Response response2 = (org.opensaml.saml2.core.Response)samlReader.readFromFile(inputSource);
//To fetch the digital signature from the response.
Signature signature = response2.getSignature();
X509Certificate certificate = (X509Certificate) keyStore.getCertificate(domainName);
//pull out the public key part of the certificate into a KeySpec
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(certificate.getPublicKey().getEncoded());
//get KeyFactory object that creates key objects, specifying RSA - java.security.KeyFactory
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//generate public key to validate signatures
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
//we have the public key
BasicX509Credential publicCredential = new BasicX509Credential();
//add public key value
publicCredential.setPublicKey(publicKey);
//create SignatureValidator
SignatureValidator signatureValidator = new SignatureValidator(publicCredential);
//try to validate
try{
signatureValidator.validate(signature);
catch(Exception e){
//
}
Now fetch the assertion map :
samlDetailsMap = setSAMLDetails(response2);
In the above logic use the below private method to pull all the assertion attributes. Finally you will have map of all the fields that are sent to you.
private Map<String, String> setSAMLDetails(org.opensaml.saml2.core.Response response2){
Map<String, String> samlDetailsMap = new HashMap<String, String>();
try {
List<Assertion> assertions = response2.getAssertions();
LOGGER.error("No of assertions : "+assertions.size());
for(Assertion assertion:assertions){
List<AttributeStatement> attributeStatements = assertion.getAttributeStatements();
for(AttributeStatement attributeStatement: attributeStatements){
List<Attribute> attributes = attributeStatement.getAttributes();
for(Attribute attribute: attributes){
String name = attribute.getName();
List<XMLObject> attributes1 = attribute.getAttributeValues();
for(XMLObject xmlObject : attributes1){
if(xmlObject instanceof XSString){
samlDetailsMap.put(name, ((XSString) xmlObject).getValue());
LOGGER.error("Name is : "+name+" value is : "+((XSString) xmlObject).getValue());
}else if(xmlObject instanceof XSAnyImpl){
String value = ((XSAnyImpl) xmlObject).getTextContent();
samlDetailsMap.put(name, value);
}
}
}
}
}
} catch (Exception e) {
LOGGER.error("Exception occurred while setting the saml details");
}
LOGGER.error("Exiting from setSAMLDetails method");
return samlDetailsMap;
}
Add new class SAMLReader as below :
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.opensaml.DefaultBootstrap;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.io.UnmarshallingException;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class SAMLReader {
private static DocumentBuilder builder;
static{
try{
DefaultBootstrap.bootstrap ();
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance ();
factory.setNamespaceAware (true);
builder = factory.newDocumentBuilder ();
}catch (Exception ex){
ex.printStackTrace ();
}
}
/**
*
* #param filename
* #return
* #throws IOException
* #throws UnmarshallingException
* #throws SAXException
*/
public XMLObject readFromFile (String filename)
throws IOException, UnmarshallingException, SAXException{
return fromElement (builder.parse (filename).getDocumentElement ());
}
/**
*
* #param is
* #return
* #throws IOException
* #throws UnmarshallingException
* #throws SAXException
*/
public XMLObject readFromFile (InputStream is)
throws IOException, UnmarshallingException, SAXException{
return fromElement (builder.parse (is).getDocumentElement ());
}
/**
*
* #param is
* #return
* #throws IOException
* #throws UnmarshallingException
* #throws SAXException
*/
public XMLObject readFromFile (InputSource is)
throws IOException, UnmarshallingException, SAXException{
return fromElement (builder.parse (is).getDocumentElement ());
}
/**
*
* #param element
* #return
* #throws IOException
* #throws UnmarshallingException
* #throws SAXException
*/
public static XMLObject fromElement (Element element)
throws IOException, UnmarshallingException, SAXException{
return Configuration.getUnmarshallerFactory ()
.getUnmarshaller (element).unmarshall (element);
}
}
The steps you've listed are more or less correct. The only thing I'd point to is that you have to be careful with the meaning if the word sends (ex. in "SP ... sends a SAML authentication request to IdP"). SAML allows authentications scenarios with zero direct communication between SP and IdP.
Another small addition is that SP may also sign his request, so you may have signature validation on both sides. Validation on the SP side is obligatory.
If you want to implement SAML, you may want to check one of the existing solutions, for example Shibboleth. If you're on platforms like Spring and JBoss you may want to check Spring Security SAML or JBoss PicketLink. If you want to go lower-level, check OpenSAML.
In my corp we have JBoss as standard and are very happy with PicketLink.

Ksoap2 - Android Build SOAP Request with Multiple Namespace definitions

I am building SOAP Request Message using KSoap2-android version 3.3.0
My second namespace for Element 'n1' not added even though i added it with
SoapEnvelope.setMapping()
<in1 xmlns:n1="http://event.api.company.com">
.....
I am expecting the Request Message (CORRECT) in this Format:
<v:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:v="http://schemas.xmlsoap.org/soap/envelope/">
<v:Header/>
<v:Body>
<updateStatus xmlns="http://service.api.company.com"
id="o0" c:root="1">
<in0>1604509</in0>
<in1 xmlns:n1="http://event.api.company.com">
<n1:actionDate>2014-09-02T05:18:20.156+0000</n1:actionDate>
<n1:ActionTypeDTO>
<n1:id>1</n1:id>
<n1:name>Other</n1:name>
<n1:description>Enter the description of the action taken.</n1:description>
</n1:ActionTypeDTO>
<n1:description>notes1</n1:description>
<n1:name>system</n1:name>
</in1>
<in2>NEW</in2>
</updateStatus>
</v:Body>
But i am getting This (WRONG) after Ksoap build the Message:
<v:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:v="http://schemas.xmlsoap.org/soap/envelope/">
<v:Header/>
<v:Body>
<updateStatus xmlns="http://service.api.company.com"
id="o0" c:root="1">
<in0>1604509</in0>
<in1>
<actionDate>2014-09-02T05:18:20.156+0000</actionDate>
<ActionTypeDTO>
<id>1</id>
<name>Other</name>
<description>Enter the description of the action taken.</description>
</ActionTypeDTO>
<description>notes1</description>
<name>system</name>
</in1>
<in2>NEW</in2>
</updateStatus>
</v:Body>
This is my Function using KSOAP2-Android API:
public void updateStatus(long in0, ActionDTO in1){
SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
soapEnvelope.implicitTypes = true;
soapEnvelope.dotNet = true;
SoapObject soapReq = new SoapObject("http://service.api.company.com","updateEventStatus");
soapEnvelope.addMapping("http://event.api.company.com","in1",new ActionDTO().getClass());
soapReq.addProperty("in0",in0);
soapReq.addProperty("in1",in1);
soapEnvelope.setOutputSoapObject(soapReq);
HttpTransportSE httpTransport = new HttpTransportSE(url,timeOut);
httpTransport.call("http://service.api.company.com/updateEventStatus",
soapEnvelope);
}
See following was not added in the Soap request:
<in1 xmlns:n1="http://event.api.company.com">
<n1:actionDate>2014-09-02T05:18:20.156+0000</n1:actionDate>
.....
.......
.....
Can Anybody help me what i am doing wrong... soapEnvelope.addMapping() function
seems not working correctly. OR am i missing something setting or flag to tell KSOAP??
I removed some code to make it clean to understand, sorry for that....
Thanks in advance for the Help ...
some how figured out to solve the issue for now.
It might be Useful if somebody using 'Wsdl2Code' to generate Java Code as I am.
(WSDL2CODE.com engine will generate the classes and methods for connecting Soap Services From Android and iPhone!)
In the Wsdl2code generated DTO Classes change the following:
public void getPropertyInfo(int index,
#SuppressWarnings("rawtypes") Hashtable arg1, PropertyInfo info) {
switch (index) {
case 0:
**info.namespace = "http://event.api.company.com";**
info.type = PropertyInfo.STRING_CLASS;
info.name = "id";
break;
case 1:
**info.namespace = "http://event.api.company.com";**
info.type = PropertyInfo.STRING_CLASS;
info.name = "name";
break;
}
}
Then it will generate the SOAP Request as i mentioned in the Question (CORRECT).

WSSE (with digest) in WCF\.Net\C# - An easy way?

Disclaimer: .Net N00b
I've been beating my head against the wall for a couple of days now trying to get the security work with this external vendors web service to no avail. It turns out that they use WSSE digest security, which, in short, adds something like this to the SOAP header:
<wsse:UsernameToken wsu:Id="Example-1">
<wsse:Username> ... </wsse:Username>
<wsse:Password Type="..."> ... </wsse:Password>
<wsse:Nonce EncodingType="..."> ... </wsse:Nonce>
<wsu:Created> ... </wsu:Created>
</wsse:UsernameToken>
I started out by adding the service reference, and through many, many blog posts, stackoverflow questions fiddling with the app.config and the code. I just couldn't seem to get it right. Maybe it isn't easily possible? Maybe I just don't know Visual Studio 2010 and .Net that well, I'm not sure.
Here is what I stopped with in my app.config:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="ServiceHttpBinding" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportWithMessageCredential" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://vendorurl"
binding="basicHttpBinding" bindingConfiguration="ServiceHttpBinding"
contract="ContractName"
name="ServiceHttpPort">
</endpoint>
</client>
</system.serviceModel>
And the C#:
var someService = new ServiceClient();
someService.ClientCredentials.UserName.UserName = "username";
someService.ClientCredentials.UserName.Password = "passwordgobbletygook/somemorebase64stuff=";
#region Begin Magic
var elements = someService.Endpoint.Binding.CreateBindingElements();
var securityBindingElement = elements.Find<SecurityBindingElement>();
securityBindingElement.IncludeTimestamp = false;
someService.Endpoint.Binding = new CustomBinding(elements);
#endregion
var response = someService.webMethod(param1, param2, param3, param4);
Console.WriteLine(response);
The funny thing is, in the vendors spec, I found that they encourage the use of WSSJ, so I tried it out (in java) and I GOT IT TO WORK IN 2 HOURS
Here is what that looks like:
public class Test implements CallbackHandler {
/**
* #param args
*/
public static void main( final String[] args ) throws Throwable {
SomeService_Service someService_Service = new SomeService_Service();
SomeService someService = someService_Service.getSomeServiceHttpPort();
BindingProvider bindingProvider = (BindingProvider)someService;
Map< String, Object > requestContext = bindingProvider.getRequestContext();
requestContext.put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://vendorurl" );
Client client = ClientProxy.getClient( someService );
Endpoint endpoint = client.getEndpoint();
Map< String, Object > outProps = new HashMap< String, Object >();
outProps.put( WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN );
outProps.put( WSHandlerConstants.USER, "username" );
outProps.put( WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST );
outProps.put( WSHandlerConstants.PW_CALLBACK_REF, new Test() );
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor( outProps );
endpoint.getOutInterceptors().add( wssOut );
System.out.println( someService.webMethod(param1, param2, param3, param4) );
}
public void handle( final Callback[] callbacks ) throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback)callbacks[ 0 ];
// set the password for our message.
pc.setPassword( "passwordgobbletygook/somemorebase64stuff=" );
}
}
Has anyone out there in stackoverflow land got this to work in .Net\C#? Is there something obvious I'm missing here?
We've run into this problem before when trying to connect a .NET based component to a JAVA based SOAP service. Our solution doesn't involve any XML construction and is IMHO a bit cleaner than anything else I've seen.
The downside is that you need to download and include an older optional .NET DLL to make it work. The upside is that the code is quite clean and fits naturally into WCF.
The basic implementation looks something like this:
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
//Class from WSE 3.0
UsernameToken token = new UsernameToken("MY_USERNAME", "MY_PASSWORD", PasswordOption.SendHashed);
//Add Auth to SOAP Header
MessageHeader header
= MessageHeader.CreateHeader(
"Security",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
token.GetXml(new XmlDocument())
);
OperationContext.Current.OutgoingMessageHeaders.Add(header);
//Build Request
OrgWS.OrganizationDetailsRequest request = new OrgWS.OrganizationDetailsRequest()
{
ID = 1
};
//Send Request
OrgWS.OrganizationDetail[] response = client.getOrganizationDetail(request);
//Do something with response
}
A full explanation can be found here: http://cxdeveloper.com/article/implementing-ws-security-digest-password-nonce-net-40-wcf
Andy's answer is spot on! Spent most of the say on this, there is a lot out there but this is the ONLY answer that worked for me. Perfect for adding nonce with passwordDigest in SOAP wsse headers. Agree with Nick V, this answer should get more recognition.
BasicHttpBinding myBinding = new BasicHttpBinding();
myBinding.Security.Mode = BasicHttpSecurityMode.Transport;
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
EndpointAddress ea = new EndpointAddress("****");
WebServiceServiceClient cc = new WebServiceServiceClient(myBinding, ea);
cc.Open();
using (OperationContextScope scope = new OperationContextScope(cc.InnerChannel))
{
//Class from WSE 3.0
UsernameToken token = new UsernameToken("userid", "password", PasswordOption.SendHashed);
//Add Auth to SOAP Header
MessageHeader header
= MessageHeader.CreateHeader(
"Security",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
token.GetXml(new XmlDocument())
);
OperationContext.Current.OutgoingMessageHeaders.Add(header);
try
{
//call SOAP methos
}
catch (Exception ex)
{
//catch any errors
}
}

removing mustUnderstand attribute from soap headers

How to remove mustunderstand attribute from soap header in axis client.even i dont set it especially, when i set soap header info mustundertand and actor attributes are automatically added to soap message.Does anybody know how to remove them ?
I am using Axis2 1.4 version's wsdl2java to create my ws client.
None of those solutions worked for me, as:
I am working with Axis (not Axis2)
I don't want the attribute to be specified at all, as my server counterpart doesn't support it ("The 'http://schemas.xmlsoap.org/soap/envelope/:mustUnderstand' attribute is not declared" is a typical answer in such a case).
Looking at the answer to "Adding ws-security to wsdl2java generated classes" helped me to write a solution that worked for me:
void addSecurityHeader(Stub stub, final String username, final String password) {
QName headerName = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security"); // Or any other namespace that fits in your case
AtomicReference<SOAPHeaderElement> header
= new AtomicReference<SOAPHeaderElement>
(new SOAPHeaderElement(headerName) {
{
SOAPElement utElem = addChildElement("UsernameToken");
utElem.addChildElement("Username").setValue(username);
utElem.addChildElement("Password").setValue(password);
}
#Override
public void setAttribute(String namespace, String localName, String value) {
if (!Constants.ATTR_MUST_UNDERSTAND.equals(localName)) { // Or any other attribute name you'd want to avoid
super.setAttribute(namespace, localName, value);
}
}
});
SOAPHeaderElement soapHeaderElement = header.get();
soapHeaderElement.setActor(null); // No intermediate actors are involved.
stub.setHeader(soapHeaderElement); // Finally, attach the header to the stub
}
If you want to disable the must understand check in the AXIS client you have
to add the following line to your code:
_call.setProperty(Call.CHECK_MUST_UNDERSTAND, new Boolean(false));
then the MustUnderstandChecker of the AXIS Client is never invoked.
In my case it worked manually remove the attribute from the SOAPHeader
SOAPHeader header = env.getHeader();
OMChildrenQNameIterator childrenWithName = (OMChildrenQNameIterator) header.getChildrenWithName(omElementauthentication.getQName());
while (childrenWithName.hasNext()) {
org.apache.axiom.om.OMElement omElement = (org.apache.axiom.om.OMElement) childrenWithName.next();
QName mustAnderstandQName = omElement.resolveQName("soapenv:mustUnderstand");
if (mustAnderstandQName == null) {
continue;
}
OMAttribute mustAnderstandAttribute = omElement.getAttribute(mustAnderstandQName);
if (mustAnderstandAttribute == null) {
continue;
}
omElement.removeAttribute(mustAnderstandAttribute);
}
I was recently struggling with similar situation and by doing some google-ing I managed to find the following solution.
Having used Axis2 you would've probably generated a MyWSStub file that contains the calls to your service.
Create an wrapper class (better not touch the auto-generated stub files) that extends your stub e.g. MyWSStubWrapper:
public class MyWSStubWrapper extends MyWSStub {
/**
* #throws AxisFault
*/
public MyWSStubWrapper() throws AxisFault {
}
/**
* #param configurationContext
* #throws AxisFault
*/
public MyWSStubWrapper(ConfigurationContext configurationContext) throws AxisFault {
super(configurationContext);
}
/**
* #param targetEndpoint
* #throws AxisFault
*/
public MyWSStubWrapper(String targetEndpoint) throws AxisFault {
super(targetEndpoint);
}
/**
* #param configurationContext
* #param targetEndpoint
* #throws AxisFault
*/
public MyWSStubWrapper(ConfigurationContext configurationContext, String targetEndpoint) throws AxisFault {
super(configurationContext, targetEndpoint);
}
/**
* #param configurationContext
* #param targetEndpoint
* #param useSeparateListener
* #throws AxisFault
*/
public MyWSStubWrapper(ConfigurationContext configurationContext, String targetEndpoint, boolean useSeparateListener) throws AxisFault {
super(configurationContext, targetEndpoint, useSeparateListener);
}
#Override
protected void addHeader(OMElement omElementToadd, SOAPEnvelope envelop, boolean mustUnderstand) {
SOAPHeaderBlock soapHeaderBlock = envelop.getHeader().addHeaderBlock(omElementToadd.getLocalName(), omElementToadd.getNamespace());
OMNode omNode = null;
// add child elements
for (Iterator iter = omElementToadd.getChildren(); iter.hasNext();) {
omNode = (OMNode) iter.next();
iter.remove();
soapHeaderBlock.addChild(omNode);
}
OMAttribute omatribute = null;
// add attributes
for (Iterator iter = omElementToadd.getAllAttributes(); iter.hasNext();) {
omatribute = (OMAttribute) iter.next();
soapHeaderBlock.addAttribute(omatribute);
}
}
}
Bear in mind that the above code will completely remove the soapenv:mustUnderstand="0|1" from your headers if you wish to added you need to call soapHeaderBlock.setMustUnderstand(true|false); somewhere in your code.
1) Are you using the Axis SOAPHeaderElement and if so, are you setting the mustUnderstand setter to false?
2) Since you're generating your client with wsdl2java, it's quite possible that the WSDL (or more accurately, the schema) contains the mustUnderstand attribute on an element referenced in your SOAP binding. So when wsdlToJava generates the client code, those attributes will naturally be added. See here for a description of the mustUnderstand attribute.
If modifying the WSDL is out of the question, and you must remove this attribute from the header, then I suppose you can try to do it with a handler
3) Not advisable, but if you really MUST remove this attribute then I suppose you can add a client side handler that alters the header: http://ws.apache.org/axis/java/apiDocs/org/apache/axis/handlers/package-summary.html
i am using axis 1.4 client with ws security
in my case as Reinhard said
this worked
MyService service = new MyServiceLocator();
MyServicePortType port = service.getMyServiceHttpsSoap11Endpoint();
((Stub) port)._setProperty(Call.CHECK_MUST_UNDERSTAND, Boolean.FALSE);

Categories