CXF With UsernameToken (WS-Security Policy)

CXF With UsernameToken (WS-Security Policy) explains about step by step details of securing a Web service using UsernameToken Profile
WS-SecurityPolicy is the binding and/or operation used in the wsdl, a WS-Policy fragment that describes the basic security requirements for interacting consumer.
Here we are implementing security policy by CXF UsernameToken
CXF gives two different techniques for adding UsernameToken 1) WS-SecurityPolicy -> In WS-SecurityPolicy, we are applying on WSDL level, Here we are defining WS-SecurityPolicy elements on WSDL and implement the security mechanism. This could be the best approach because client can able to understand which security mechanism is implemented and client can able to proceed accordingly. 2) Using CXF interceptors -> Here we are manually adding security mechanism to CXF interceptors,the problem on this approach client cant able to understand which security mechanism is implemented because it is not available on wsdl
Required Libraries
You need to download
Following jar must be in ClassPath
- commons-logging-1.1.1.jar
- cxf-2.7.3.jar
- httpasyncclient-4.0-beta3.jar
- httpclient-4.2.1.jar
- httpcore-4.2.2.jar
- httpcore-nio-4.2.2.jar
- jaxb-api-2.2.6.jar
- jaxb-impl-2.2.6.jar
- neethi-3.0.2.jar
- spring-aop-3.0.7.RELEASE.jar
- spring-asm-3.0.7.RELEASE.jar
- spring-beans-3.0.7.RELEASE.jar
- spring-context-3.0.7.RELEASE.jar
- spring-core-3.0.7.RELEASE.jar
- spring-expression-3.0.7.RELEASE.jar
- spring-web-3.0.7.RELEASE.jar
- wsdl4j-1.6.2.jar
- wss4j-1.6.9.jar
- xmlschema-core-2.0.3.jar
- xmlsec-1.5.3.jar
CXF With UsernameToken(WS-security policy) Example
I am creating a sample web service project that pass Student object and return with some changes on that object. The service is using simple POJO (Plain Old Java Object) bean.
Firstly create a Dynamic Web Project (File->New->Dynamic Web Project) named "CXFTutorial" according to following screenshot
Create a Student Object
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Create a Service Interface
This service interface will defines which methods of web service, to be invoked by the client
import javax.jws.WebService;
@WebService
public interface ChangeStudentDetails {
Student changeName(Student student);
}
Implement the Service Interface
Here we implement the service interface created on the previous step
import javax.jws.WebService;
@WebService(endpointInterface = "com.student.ChangeStudentDetails")
public class ChangeStudentDetailsImpl implements ChangeStudentDetails {
public Student changeName(Student student) {
student.setName("Hello "+student.getName());
return student;
}
}
Create ServerPasswordCallback
Here we are created myPasswordCallback bean in order to check the username token credentials
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 ServerPasswordCallback implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
if ("joe".equals(pc.getIdentifier())) {
System.out.println("pc.getPassword() " + pc.getPassword());
pc.setPassword("joespassword");
}
}
}
Create ChangeStudent.wsdl
Here we are creating a wsdl file in order to utilize contract first approach, We also using WS-Security with UsernameTokens. You can see this declaration on wsdl file
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://student.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsaws="http://www.w3.org/2005/08/addressing" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:sp13="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200802" name="ChangeStudentDetailsImplService" targetNamespace="http://student.com/"> <wsdl:types> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://student.com/" elementFormDefault="unqualified" targetNamespace="http://student.com/" version="1.0"> <xs:element name="Student" type="tns:student"></xs:element> <xs:element name="changeName" type="tns:changeName"></xs:element> <xs:element name="changeNameResponse" type="tns:changeNameResponse"></xs:element> <xs:complexType name="changeName"> <xs:sequence> <xs:element minOccurs="0" name="arg0" type="tns:student"></xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="student"> <xs:sequence> <xs:element minOccurs="0" name="name" type="xs:string"></xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="changeNameResponse"> <xs:sequence> <xs:element minOccurs="0" name="return" type="tns:student"></xs:element> </xs:sequence> </xs:complexType> </xs:schema> </wsdl:types> <wsdl:message name="changeName"> <wsdl:part element="tns:changeName" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:message name="changeNameResponse"> <wsdl:part element="tns:changeNameResponse" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:portType name="ChangeStudentDetails"> <wsdl:operation name="changeName"> <wsdl:input message="tns:changeName" name="changeName"> </wsdl:input> <wsdl:output message="tns:changeNameResponse" name="changeNameResponse"> </wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name="ChangeStudentDetailsImplServiceSoapBinding" type="tns:ChangeStudentDetails"> <wsp:PolicyReference URI="#ChangeStudentDetailsImplServiceSoapBindingPolicy" /> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"></soap:binding> <wsdl:operation name="changeName"> <soap:operation soapAction="" style="document"></soap:operation> <wsdl:input name="changeName"> <soap:body use="literal"></soap:body> </wsdl:input> <wsdl:output name="changeNameResponse"> <soap:body use="literal"></soap:body> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="ChangeStudentDetailsImplService"> <wsdl:port binding="tns:ChangeStudentDetailsImplServiceSoapBinding" name="ChangeStudentDetailsImplPort"> <wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#ChangeStudentDetailsImplServiceSoapBindingPolicy" /> <soap:address location="http://localhost:8080/CXFTutorial/ChangeStudent"></soap:address> </wsdl:port> </wsdl:service> <wsp:Policy wsu:Id="ChangeStudentDetailsImplServiceSoapBindingPolicy" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <wsp:ExactlyOne> <wsp:All> <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy> <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"> <wsp:Policy> <sp:WssUsernameToken11 /> </wsp:Policy> </sp:UsernameToken> </wsp:Policy> </sp:SupportingTokens> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> </wsdl:definitions>
Create a cxf.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:cxf="http://cxf.apache.org/core" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:soap="http://cxf.apache.org/bindings/soap" xmlns:wsa="http://cxf.apache.org/ws/addressing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" id="loggingInInterceptor" /> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" id="logOutInterceptor" /> <cxf:bus> <cxf:inInterceptors> <ref bean="loggingInInterceptor" /> </cxf:inInterceptors> <cxf:outInterceptors> <ref bean="logOutInterceptor" /> </cxf:outInterceptors> </cxf:bus> <jaxws:endpoint address="/ChangeStudent" id="changeStudent" implementor="com.student.ChangeStudentDetailsImpl" wsdlLocation="WEB-INF/ChangeStudent.wsdl"> <jaxws:properties> <entry key="ws-security.callback-handler" value-ref="myPasswordCallback" /> </jaxws:properties> </jaxws:endpoint> <!--Added callback for checking the usernametoken credential --> <bean class="com.student.ServerPasswordCallback" id="myPasswordCallback" /> </beans>
web.xml
Change the web.xml file to find CXF servlet and cxf.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/cxf.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
Publishing CXF Web Service
Deployed CXF Web Service
Testing CXF UsernameToken Example With soapUI
Here we are using soapUI for testing CXF UsernameToken example
Run
On following screen shot, you can see the blue lines, which is specifying username as "joe", password as "joespassword" and WSS-Password Type as "PasswordText"
Run
You can also use wsimport tool for testing this service. Only thing need to change is modify the Main class according to below class
import java.util.Map;
import com.student.ChangeStudentDetails;
import com.student.ChangeStudentDetailsImplService;
import com.student.Student;
import javax.xml.ws.BindingProvider;
//CXF UsernameToken Example
public class Main {
public static void main(String[] args) {
ChangeStudentDetailsImplService service = new ChangeStudentDetailsImplService();
ChangeStudentDetails changeStudentDetailsImplPort = service.getChangeStudentDetailsImplPort();
Map ctx = ((BindingProvider) changeStudentDetailsImplPort).getRequestContext();
ctx.put("ws-security.username", "joe");
ctx.put("ws-security.password", "joespassword");
Student student = new Student();
student.setName("Rockey");
student = changeStudentDetailsImplPort.changeName(student);
System.out.println(student.getName());
}
}
Output
Hello Rockey