Java – usernametoken WS security (WSS4J) annotated with Apache CXF
I'm trying to create a "Java first" WebService that will use a simple usernametoken WS security I tried to follow the example of CXF When I query my WSDL, I can't see anything related to WS security I'm using CXF 2.7 5. I'm trying to do everything with annotations
Here are my failed attempts:
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>
Solution
You can use WS securitypolicy based configuration instead of WSS4J interceptor method!
To do this, create one from the "Java first" web service WSDL file, and use and partially extend it, and put it anywhere in the 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 in the @ WebService annotation and use the @ endpointconfig annotation instead of @ 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(); } }
In WEB-INF / jaxws endpoint config Ws security. XML callback-handler.
<?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 org apache. ws. Security JBoss module: WEB-INF / jboss-precipitation-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 HelloWorld Projekt: https://github.com/matyig/wsse-policy-username
If you want to use a non WS securitypolicy method, you can use the spring XML configuration method You can find a good tutorial here:
http://www.jroller.com/gmazza/entry/cxf_usernametoken_profile