In the following code sample, a SOAP message is parsed to extract the username and password credentials, these values are applied to the MessageContext class for the invocation (and in the case of WebSphere, a login is performed), and control is returned to the superclass. Also a locale is specified for the call by setting property locale in the MessageContext object. The code sample is followed by an example of a SOAP message which would be processed by the code.
package webservice; import curam.util.connectors.webservice.CuramEJBMethodProvider; import curam.util.resources.Configuration; import curam.util.resources.EnvironmentConstants; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.security.PrivilegedAction; import java.util.Iterator; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.LoginContext; import javax.xml.soap.Name; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPHeader; import org.apache.axis.AxisFault; import org.apache.axis.Message; import org.apache.axis.MessageContext; import org.apache.axis.message.SOAPEnvelope; import org.apache.axis.providers.java.EJBProvider; /** * A web services hook which extends the Axis EJB provider to * enable the developer to access the SOAP message. In this case * it takes the username and password from the SOAP header and * sets them in the method call. * */ public class TestmodelProvider extends CuramEJBMethodProvider { /** The name of an XML attribute in a multi ref element. */ private static final String kNameOfIdAttribute = "id"; /** The name of an XML element in the SOAP body. */ private static final String kMultiRefElementName = "multiRef"; /** * The name of the attribute containing a `href` to another * element. */ private static final String kHrefAttributeName = "href"; /** * The name of the header element as defined in the WSDL file. */ private static final String kNameOfHeaderElement = "inHeader"; /** The name of the element containing the user name field. */ private static final String kUsernameFieldName = "userName"; /** The name of the element containing the password field. */ private static final String kPasswordFieldName = "password"; /** Cached Do As Method instance. */ private static Method stSubjectDoAsMethod; /** * Hook which gets the credentials from the header of the soap * message and sets them in the message context for the call * before delegating back to the superclass method. * * @param msgContext The message context for the call. * * @throws AxisFault Generic Axis exception. */ public void invoke(final MessageContext msgContext) throws AxisFault { final Message requestMessage = msgContext.getRequestMessage(); final SOAPEnvelope envelope = requestMessage.getSOAPEnvelope(); final SOAPElement element = getSoapElement(envelope, kNameOfHeaderElement); String userName = null; String password = null; try { // Get parameters from the SOAP header and set them in the // message context for the call. userName = getSubElementValue(element, envelope.createName( kUsernameFieldName)); password = getSubElementValue(element, envelope.createName( kPasswordFieldName)); // Check the soundness of our SOAP header processing before // we attempt to use the data for real. Otherwise bad or // missing data in these variables will simply manifest // itself misleadingly as a security configuration problem. if ((userName == null) || (userName.length() == 0) || (password == null) || (password.length() == 0)) { final AxisFault e = new AxisFault( "Bad username/password in SOAP" + " header '" + userName + "'/'" + password + "'"); throw e; } msgContext.setUsername(userName); msgContext.setPassword(password); // Specify an absolute locale for the invocation: final String localeFrenchCanada = "fr_CA"; msgContext.setProperty("locale", localeFrenchCanada); } catch (SOAPException e) { throw new AxisFault(e.getMessage(), e); } if (isRunningInWebSphere()) { // A WebSphere limitation means that it will not // automatically see the credentials we have just set, we // must also perform our login here. try { final Method doAsMethod = getDoAsMethod(); final LoginContext loginContext = getLoginContext(userName, password); loginContext.login(); final Subject subject = loginContext.getSubject(); // Create a privileged action class which includes all the // information about this call. final PrivilegedAction action = new ProviderPrivilegedAction(this, msgContext); final Object[] parameterValues = {subject, action}; // invoke the rest of the call under the new credentials. final Object axisFault = doAsMethod.invoke(null, parameterValues); // Exceptions cannot be thrown from the above invocation, // they are returned instead. If one was returned then // throw it now. if (axisFault != null) { throw new AxisFault("" + axisFault, (Exception) axisFault); } } catch (Exception e) { throw new AxisFault(e.getMessage(), e); } } else { // Not in WebSphere. Simply delegate straight through. super.invoke(msgContext); } } /** * An accessor for the invoke method in the superclass. Required * because it must be invoked by another class - the inner class * within this one. * * @param msgContext The message context object for this call. * * @throws AxisFault Generic Axis fault handler. */ private void superInvoke(final MessageContext msgContext) throws AxisFault { super.invoke(msgContext); } /** * Indicates whether we are running within WebSphere in which * case we must delegate the rest of the call as a privileged * action. * * @return True if running under WebSphere, false otherwise. */ private boolean isRunningInWebSphere() { final String vendorName = System.getProperty("java.vendor"); return vendorName.startsWith("IBM"); } /** * Gets a named element from the SOAP message, searching both * the body and header. * * @param envelope The SOAP envelope. * @param elementNameString The name of the element to get. * * @return The required element or null if it was not found. */ private SOAPElement getSoapElement(final SOAPEnvelope envelope, final String elementNameString) { SOAPElement result = null; try { final Name elementName = envelope.createName(elementNameString); final Name hrefAttributeName = envelope.createName(kHrefAttributeName); final SOAPHeader sh = envelope.getHeader(); final SOAPBody sb = envelope.getBody(); // first search the header. SOAPElement candidateElement = null; final Iterator headerIterator = sh.getChildElements(elementName); if (headerIterator.hasNext()) { candidateElement = (SOAPElement) headerIterator.next(); } // search the body, if necessary. if (candidateElement == null) { final Iterator bodyIterator = sb.getChildElements(); if (bodyIterator.hasNext()) { candidateElement = (SOAPElement) bodyIterator.next(); } } // Now we need to check if this is literal or encoded // element. A literal one is embedded directly, an encoded // one means that this element is simply a pointer to an // element elsewhere in the message. if (candidateElement != null) { final String hrefValue = candidateElement.getAttributeValue(hrefAttributeName); if ((hrefValue != null) && (hrefValue.length() > 0)) { // it points to a multi ref, so get this instead. result = getMultiRefElement(envelope, hrefValue); } else { // It's literal so return it directly. result = candidateElement; } } } catch (SOAPException e) { e.printStackTrace(); } return result; } /** * Gets a multi ref element from a SOAP message. * * @param envelope The SOAP envelope. * @param idStringWithPrefix The identifier of the multi ref * element. * * @return The matching element, or null if it was not found. * * @throws SOAPException If any SOAP error occurs. */ private SOAPElement getMultiRefElement( final SOAPEnvelope envelope, final String idStringWithPrefix) throws SOAPException { SOAPElement result = null; // Remove the hash character: final String idString = idStringWithPrefix.substring(1); final Name idName = envelope.createName(kNameOfIdAttribute); final SOAPBody body = envelope.getBody(); final Iterator multiRefIterator = body.getChildElements( envelope.createName(kMultiRefElementName)); while (multiRefIterator.hasNext()) { final Object o = multiRefIterator.next(); final SOAPElement currentElement = (SOAPElement) o; final String currentId = currentElement.getAttributeValue(idName); if (currentId.equals(idString)) { result = currentElement; break; } } return result; } /** * Gets the value of a specified element within the given * element. If multiple occurrences are present, the first one * is returned. * * @param element The element containing the required one. * @param elementName The name of the required element. * * @return The string value of the element, or null if the * specified sub element does not exist. */ private String getSubElementValue(final SOAPElement element, final Name elementName) { String result = null; final Iterator elementIterator = element.getChildElements(elementName); if (elementIterator.hasNext()) { final SOAPElement subElement = (SOAPElement) elementIterator.next(); result = subElement.getValue(); } return result; } /** * Gets the hidden implementation class for the Login Context. * * @param userName The user name to login with. * @param password The password to login with. * * @return class for implementation * * @throws Exception if an error occurs getting an instance of * the LoginContext class */ private LoginContext getLoginContext( final String userName, final String password) throws Exception { final LoginContext resultLoginContext; // Initialize WebSphere specific callback handler. Use // reflection to avoid a build time dependency on an IBM // class. final Class wsCallbackHandlerClass = Class.forName( EnvironmentConstants.kWSCallbackHandlerImplClassName); final Class[] parameters = { String.class, String.class }; final Constructor constructor = wsCallbackHandlerClass.getConstructor(parameters); final Object[] parameterValues = {userName, password}; // The WebSphere login resultLoginContext = new LoginContext( EnvironmentConstants.kWSLogin, (CallbackHandler) constructor.newInstance( parameterValues)); return resultLoginContext; } /** * Gets the cached Do As method, initializing it if necessary. * * @return The Do As method for this server. * * @throws Exception If the method could not be obtained for * any reason. */ private Method getDoAsMethod() throws Exception { if (stSubjectDoAsMethod != null) { return stSubjectDoAsMethod; } final Class wsSubjectClass = Class.forName(EnvironmentConstants.kWSSubjectClassName); final Class[] moreParameters = { Subject.class, PrivilegedAction.class }; stSubjectDoAsMethod = wsSubjectClass.getDeclaredMethod( EnvironmentConstants.kDoAsMethodName, moreParameters); return stSubjectDoAsMethod; } /** * * */ private class ProviderPrivilegedAction implements PrivilegedAction { /** The message context for the call. */ private final MessageContext msgContext; /** The class whose method we must invoke. */ private final TestmodelProvider ownerObject; /** * Constructor which initializes the fields. * * @param newOwnerObject The class whose method we will * invoke. * @param newMsgContext The message context for the call. */ public ProviderPrivilegedAction( final TestmodelProvider newOwnerObject, final MessageContext newMsgContext) { ownerObject = newOwnerObject; msgContext = newMsgContext; } /** * Runs the privileged action using the fields of this class. * * @return The exception resulting from the call, or null if * none was thrown. */ public Object run() { Object resultFault = null; try { ownerObject.superInvoke(msgContext); } catch (Exception e) { resultFault = e; } return resultFault; } } }
The text below shows an actual SOAP message (with some formatting for readability) which is processed by the Java code above. Note that the SOAP header refers to a parameter named ` inCred ` which contains the username and password credentials. The actual data is not stored literally in the header but in a ` multiRef ` element in the message body.
<soapenv:Envelope xmlns:soapenv= "http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Header> <inCred href="#id0" xmlns=""/> </soapenv:Header> <soapenv:Body soapenc:encodingStyle= "http://schemas.xmlsoap.org/soap/encoding/"> <opDemo xmlns="http://remote.feature"> <in1 href="#id1" xmlns=""/> </opDemo> <multiRef id="id1" soapenc:root="0" soapenv:encodingStyle= "http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns-905576305:PersonDetailsWrapper" xmlns:ns-905576305="http://feature/struct/" xmlns=""> <firstName xsi:type="xsd:string">Jimmy</firstName> <idNumber xsi:type="xsd:string">0000361i</idNumber> <surname xsi:type="xsd:string">Client</surname> </multiRef> <multiRef id="id0" soapenc:root="0" soapenv:encodingStyle= "http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns-905576305:CredentialsWrapper" xmlns:ns-905576305="http://feature/struct/" xmlns=""> <password xsi:type="xsd:string">password</password> <userName xsi:type="xsd:string">superuser</userName> </multiRef> </soapenv:Body> </soapenv:Envelope>