In a previous post I described how to enable WSSE security options in your web services proxied by Mule ESB. In this post I show you how to enable WSSE basic authentication on a ‘client’ side. I used this set up when I needed to consume services from Oracle CRM On Demand.
There are different ways to implement this. The most convenient one (the one I will show here) is by letting the CXF framework handle the security part, just like a framework should work.
So lets get down to the code and configuration. First I will show you the Mule configuration to enable the necessary CXF interceptor in the Web Service client proxy:
<spring:bean id="myPasswordCallback" class="net.pascalalma.ws.PasswordCallback"> <spring:property name="passwordPropertyName" value="my.ws.password" /> </spring:bean> <https:endpoint name="secured-service" host="https://some.provider.url" port="488" path="some/path/" method="POST" responseTimeout="5000" keep-alive="true" connector-ref="my-http-connector" exchange-pattern="request-response"> <cxf:proxy-client payload="body" enableMuleSoapHeaders="false" soapVersion="1.1" > <cxf:outInterceptors> <spring:bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <spring:constructor-arg> <spring:map> <spring:entry key="action" value="UsernameToken" /> <spring:entry key="user" value="${my.ws.username}" /> <spring:entry key="passwordType" value="PasswordText" /> <!-- The callback supplies the password so its not stored in our config file --> <spring:entry key="passwordCallbackRef" value-ref="myPasswordCallback" /> </spring:map> </spring:constructor-arg> </spring:bean> </cxf:outInterceptors> </cxf:proxy-client> </https:endpoint>
The main component here is the configuration of the WSS4JOutInterceptor. This interceptor takes care of the generation of the necessary security elements. The action I configured here is the UsernameToken because we only want to generate a username and password in the header of the SOAP message. To obtain the password I defined a class that I created as the PasswordCallbackHandler. This callbackHandler simply gets the password from a resource bundle based on the supplied password-property name. Of course you can choose a different approach here. The implementation of this callbackHandler looks like:
package net.pascalalma.ws; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.ws.security.WSPasswordCallback; import java.io.IOException; import java.util.ResourceBundle; /** * Looks up the password by using the supplied propertyName */ public class PasswordCallback implements CallbackHandler { static final ResourceBundle bundle = ResourceBundle.getBundle("my-environment"); private String passwordPropertyName; public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; pc.setPassword(bundle.getString(getPasswordPropertyName())); } public String getPasswordPropertyName() { return passwordPropertyName; } public void setPasswordPropertyName(String passwordPropertyName) { this.passwordPropertyName = passwordPropertyName; } }
With this configuration in place each SOAP call to this service will get a SOAP Header with the following elements in it:
<soap:Header> <wsse:Security soap:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1. 0.xsd"> <wsse:UsernameToken wsu:Id="UsernameToken-375"> <wsse:Username>PascalAlma</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">MySecretPassword</wsse:Password> </wsse:UsernameToken> </wsse:Security> </soap:Header>