I'm trying to create a "java first" webservice that will use plain and simple UsernameToken WS-Security. I've attempted to follow the examples from CXF. When I query my wsdl I see no mention of anything ws-security related. I'm using CXF 2.7.5 and I'm trying to do everything with annotations.
The following is my failed attempt:
SampleService.java:
import java.util.ArrayList;
import java.util.Date;
import javax.jws.WebParam;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
@WebService(targetNamespace="https://test.company.com/ws/")
@SOAPBinding(style = SOAPBinding.Style.RPC)
@EndpointProperties({
@EndpointProperty(key = "action", value="UsernameToken"),
@EndpointProperty(key = "passwordType", value="PasswordText"),
@EndpointProperty(key = "ws-security.callback-handler", value="PasswordHandler"),
//@EndpointProperty(key = "ws-security.validate.token", value="false"),
})
public interface SampleService {
@WebMethod
public String getSample(
@WebParam(name="startDate") Date startDate,
@WebParam(name="endDate") Date endDate);
}
SampleServiceImpl.java:
import java.util.Date;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService(endpointInterface = "SampleService", targetNamespace="https://test.company.com/ws/")
public class SampleServiceImpl implements SampleService {
@Override
@WebMethod
public String getSample(Date startDate, Date endDate) {
StringBuilder sb = new StringBuilder();
sb.append("Start Date: ");
sb.append(startDate.toString());
sb.append("\n");
sb.append("End Date: ");
sb.append(endDate.toString());
return sb.toString();
}
}
PasswordHandler.java:
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class PasswordHandler implements CallbackHandler {
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
System.out.println("User: " + pc.getIdentifier());
System.out.println("Password: " + pc.getIdentifier());
System.out.println("Type: " + pc.getType());
if (pc.getIdentifier().equals("joe")) {
// set the password on the callback. This will be compared to the
// password which was sent from the client.
pc.setPassword("password");
}
}
}
SampleServicePublisher.java:
import java.util.HashMap;
import java.util.Map;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.handler.WSHandlerConstants;
public class SampleServicePublisher {
public static void main(String[] args) {
String URL = "http://localhost:9999/ws/SampleService";
EndpointImpl jaxWsEndpoint =
(EndpointImpl) javax.xml.ws.Endpoint.publish(URL, new SampleServiceImpl());
Endpoint cxfEndpoint = jaxWsEndpoint.getServer().getEndpoint();
Map<String,Object> inProps= new HashMap<String,Object>();
// how to configure the properties is outlined below;
WSS4JInInterceptor wssIn = new WSS4JInInterceptor(inProps);
cxfEndpoint.getInInterceptors().add(wssIn);
Map<String,Object> outProps = new HashMap<String,Object>();
// how to configure the properties is outlined below;
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
cxfEndpoint.getOutInterceptors().add(wssOut);
inProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
// Password type : plain text
inProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
// for hashed password use:
//properties.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
// Callback used to retrieve password for given user.
inProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, PasswordHandler.class.getName());
}
}
mvn dependencies:
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>2.7.5</version>
</dependency>
<!-- Jetty is needed if you're using the CXFServlet -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-rm</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-security</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-addr</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-policy</artifactId>
<version>2.7.5</version>
</dependency>
</dependencies>
You could use the WS-SecurityPolicy based configuration instead of the WSS4J interceptor approach!
For this create a .wsdl file from your "java first" webservice and extend it with the and part and put it anywhere in your project. (f.e. /WEB-INF/wsdl)
...
<binding name="SecurityServicePortBinding" type="tns:ServiceIface">
<wsp:PolicyReference URI="#SecurityServiceBindingPolicy"/>
....
</binding>
<service name="SecurityService">
<port name="SecurityServicePort" binding="tns:SecurityServicePortBinding">
<soap:address location="https://localhost:8443/jaxws-samples-wsse-policy-username"/>
</port>
</service>
<wsp:Policy wsu:Id="SecurityServiceBindingPolicy">
<wsp:ExactlyOne>
<wsp:All>
<wsaw:UsingAddressing
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
wsp:Optional="true" />
<sp:TransportBinding>
<wsp:Policy>
<sp:TransportToken>
<wsp:Policy>
<sp:HttpsToken
RequireClientCertificate="false" />
</wsp:Policy>
</sp:TransportToken>
<sp:Layout>
<wsp:Policy>
<sp:Lax />
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic128 />
</wsp:Policy>
</sp:AlgorithmSuite>
</wsp:Policy>
</sp:TransportBinding>
<sp:SignedSupportingTokens>
<wsp:Policy>
<sp:UsernameToken
sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10 />
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
<sp:Wss11 />
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
</definitions>
Define the wsdlLocation parameter within the @Webservice annotation and use the @EndpointConfig annotation not @EndpointProperties.
@Stateless
@WebService
(
portName = "SecurityServicePort",
serviceName = "SecurityService",
wsdlLocation = "WEB-INF/wsdl/SecurityService.wsdl",
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy",
endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.wsdl.ServiceIface"
)
@EndpointConfig(configFile = "WEB-INF/jaxws-endpoint-config.xml", configName = "Custom WS-Security Endpoint")
public class ServiceImpl implements ServiceIface
{
public String sayHello()
{
return helloservice.sayHello();
}
}
Define your ws-security.callback-handler within the WEB-INF/jaxws-endpoint-config.xml.
<?xml version="1.0" encoding="UTF-8"?>
<jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd">
<endpoint-config>
<config-name>Custom WS-Security Endpoint</config-name>
<property>
<property-name>ws-security.callback-handler</property-name>
<property-value>org.jboss.test.ws.jaxws.samples.wsse.policy.basic.UsernamePasswordCallback</property-value>
</property>
</endpoint-config>
</jaxws-config>
mvn dependencies:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-security</artifactId>
<version>${cxf.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.ws.native</groupId>
<artifactId>jbossws-native-core</artifactId>
<version>4.1.1.Final</version>
<scope>provided</scope>
</dependency>
Load the org.apache.ws.security JBOSS module: WEB-INF/jboss-depoyment-structure.xml:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
<deployment>
<dependencies>
<module name="org.apache.ws.security"/>
</dependencies>
</deployment>
</jboss-deployment-structure>
I implemented a helloworld projekt: https://github.com/matyig/wsse-policy-username
If you'd like using a Non-WS-SecurityPolicy approach, you could use the spring xml configuration way. You find a good tutorial here:
http://www.jroller.com/gmazza/entry/cxf_usernametoken_profile