Webservice Spring WS-Security

My previous blog I explain some SOA concept. Now  In this blog I am jumping to some practical and explaining how to setup some basic web services with Spring framework and how to implement some security with web Services. To run this example  you need JDK 1.5+ and spring framework 3.0+I created some basic configuration. Here are list.
1. applicationContext-service.xml — It has some basic configuration of web service in spring
2. HelloWorldWS.java — This class is exposing webservice
3. HelloWorldServiceHandler.java — This class is monitoring incoming request and outgoing message. Here we implement WS-security.
4. HelloWorldManager.java — This class  is interface for business implementation.
5. HelloWorldManagerImpl.java— This class has business implementation.Now lets start how I implemented this web service. here are codes.
1. applicationContext-service.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"
  5. xmlns:ws="http://jax-ws.dev.java.net/spring/core"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  8. http://jax-ws.dev.java.net/spring/core
  9. http://jax-ws.dev.java.net/spring/core.xsd
  10. http://jax-ws.dev.java.net/spring/servlet
  11. http://jax-ws.dev.java.net/spring/servlet.xsd">
  12. <bean id="constantMap" />
  13. <ws:service id="SOAPservice" bean="#helloWorldWS">
  14. <ws:handlers>
  15. <ref bean="helloWorldHandler" />
  16. </ws:handlers>
  17. </ws:service>
  18. <wss:bindings id="jaxWs">
  19. <wss:bindings>
  20. <wss:binding url="/webservices/HelloWorldService">
  21. <wss:service>
  22. <ref bean="SOAPservice"/>
  23. </wss:service>
  24. </wss:binding>
  25. </wss:bindings>
  26. </wss:bindings>
  27. <bean id="helloWorldHandler">
  28. <property name="constantMap" ref="constantMap" />
  29. </bean>
  30. <!-- Injecting DAO Object -->
  31. <bean id="helloWorldManager">
  32. <property name="target">
  33. <bean>
  34. <property name="userDAO"><ref bean="userDAO"/></property>
  35. </bean>
  36. </property>
  37. </bean>
  38. <bean id="helloWorldWS">
  39. <property name="helloWorldManager"><ref bean="helloWorldManager"/></property>
  40. </bean>
  41. </beans>
2. HelloWorldWS.java

  1. package com.vanrish.service;
  2. import javax.annotation.Resource;
  3. import javax.jws.WebMethod;
  4. import javax.jws.WebParam;
  5. import javax.jws.WebResult;
  6. import javax.jws.WebService;
  7. import javax.jws.soap.SOAPBinding;
  8. import javax.xml.ws.WebServiceContext;
  9. import com.vanrish.service.HelloWorldManager;
  10. import com.vanrish.xml.schema.PeopleInfoRequest;
  11. import com.vanrish.xml.schema.PeopleInfoResponse;
  12. @WebService (targetNamespace="http://www.vanrish.com/helloWorldService",serviceName = "HelloWorldService")
  13. @SOAPBinding(style=SOAPBinding.Style.DOCUMENT, use=SOAPBinding.Use.LITERAL, parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)
  14. public class HelloWorldWS {
  15. private HelloWorldManager helloWorldManager;
  16. @Resource
  17. WebServiceContext context;
  18. @WebMethod(exclude=true)
  19. public void setHelloWorldManager(HelloWorldManager helloWorldManager) {
  20. this.helloWorldManager = helloWorldManager;
  21. }
  22. @WebMethod(operationName = "getPeopleInfo")
  23. @WebResult(name = "PeopleInfo", partName = "PeopleInfo")
  24. public PeopleInfoResponse getPeopleInfo(@WebParam(name = "PeopleInfoRequest", partName = "PeopleInfoRequest",targetNamespace="http://www.vanrish.com/helloWorldService") PeopleInfoRequest peopleInfoRequest) throws Exception {
  25. return helloWorldManager.getPeopleInfo(peopleInfoRequest);
  26. }
  27. }
3. HelloWorldServiceHandler.java
  1. package com.vanrish.service.handler;
  2. import java.io.ByteArrayOutputStream;
  3. import java.util.Iterator;
  4. import java.util.Map;
  5. import java.util.Set;
  6. import javax.xml.namespace.QName;
  7. import javax.xml.soap.SOAPElement;
  8. import javax.xml.soap.SOAPEnvelope;
  9. import javax.xml.soap.SOAPHeader;
  10. import javax.xml.soap.SOAPMessage;
  11. import javax.xml.soap.SOAPPart;
  12. import javax.xml.ws.handler.MessageContext;
  13. import javax.xml.ws.handler.soap.SOAPHandler;
  14. import javax.xml.ws.handler.soap.SOAPMessageContext;
  15. import javax.xml.soap.Name;
  16. import org.apache.commons.logging.Log;
  17. import org.apache.commons.logging.LogFactory;

  18. public class HelloWorldServiceHandler implements SOAPHandler {

  19. private static final Log log = LogFactory.getLog(HelloWorldServiceHandler.class);
  20. /** The Constant USERNAME_TOKEN_STRING. */
  21. private static final String USERNAME_TOKEN_STRING = "UsernameToken";
  22. /** The Constant USERNAME_STRING. */
  23. private static final String USERNAME_STRING = "Username";
  24. /** The Constant PASSWORD_STRING. */
  25. private static final String PASSWORD_STRING = "Password";
  26. private Map constantMap;

  27. public Set getHeaders() {
  28. return null;
  29. }
  30. public void close(MessageContext context) {
  31. }
  32. public boolean handleFault(SOAPMessageContext context) {
  33. logToSystemOut(context);
  34. return true;
  35. }
  36. public boolean handleMessage(SOAPMessageContext context) {
  37. Boolean outboundProperty = (Boolean) context
  38. .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
  39. boolean isSoapRequestHandle = false;
  40. if (outboundProperty.booleanValue()) {
  41. isSoapRequestHandle = true;
  42. /* ************************************************************************
  43. * If you are manupulating outgoing header then you need to add this code
  44. *
  45. **************************************************************************
  46. * try { SOAPMessage message = context.getMessage();
  47. *
  48. * SOAPPart sp = message.getSOAPPart();
  49. *
  50. * SOAPEnvelope envelope = sp.getEnvelope();
  51. *
  52. * SOAPHeader header = envelope.addHeader();
  53. *
  54. * SOAPElement security = header.addChildElement("Security", "wsse",
  55. * "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
  56. * );
  57. *
  58. * SOAPElement usernameToken =
  59. * security.addChildElement("UsernameToken", "wsse");
  60. * usernameToken.addAttribute(new QName("xmlns:wsu"),
  61. * "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
  62. * );
  63. *
  64. * SOAPElement username = usernameToken.addChildElement("Username",
  65. * "wsse"); username.addTextNode("TestUser");
  66. *
  67. * SOAPElement password = usernameToken.addChildElement("Password",
  68. * "wsse"); password.setAttribute("Type",
  69. * "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
  70. * ); password.addTextNode("TestPassword");
  71. *
  72. * //Print out the outbound SOAP message to System.out
  73. * message.writeTo(System.out); System.out.println("");
  74. *
  75. *
  76. *
  77. * }catch (Exception e) { e.printStackTrace();
  78. *
  79. * }
  80. */
  81. } else {
  82. try {

  83. SOAPMessage message = context.getMessage();
  84. SOAPPart sp = message.getSOAPPart();
  85. SOAPEnvelope envelope = sp.getEnvelope();
  86. SOAPHeader sh = envelope.getHeader();
  87. isSoapRequestHandle = processSOAPHeader(sh);
  88. message.writeTo(System.out);
  89. if (!isSoapRequestHandle) {

  90. SOAPElement errorMessage = sh.addChildElement(
  91. "errorMessage", "error",
  92. "http://vanrish.com/helloService/error");
  93. SOAPElement error = errorMessage.addChildElement("error");
  94. error.addTextNode("Authentication Failed !!!");
  95. }
  96. } catch (Exception e) {
  97. e.printStackTrace();
  98. }
  99. }
  100. logToSystemOut(context);
  101. return isSoapRequestHandle;
  102. }
  103. private void logToSystemOut(SOAPMessageContext smc) {
  104. Boolean outboundProperty = (Boolean) smc
  105. .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
  106. if (outboundProperty.booleanValue()) {
  107. log.debug("\nOutgoing message:");
  108. } else {
  109. log.debug("\nIncoming message:");
  110. }
  111. SOAPMessage message = smc.getMessage();
  112. try {
  113. log.debug(handleRequestAndResponse(message));
  114. } catch (Exception e) {
  115. System.out.println("Exception in handler: " + e);
  116. }
  117. }
  118. private String handleRequestAndResponse(SOAPMessage msg) {
  119. ByteArrayOutputStream obj = new ByteArrayOutputStream();
  120. try {
  121. msg.writeTo(obj);
  122. return obj.toString();
  123. } catch (Exception ex) {
  124. obj = null;
  125. ex.printStackTrace();
  126. }
  127. return "";
  128. }
  129. private boolean processSOAPHeader(SOAPHeader sh) {
  130. boolean authenticated = false;
  131. // look for authentication header element inside the HEADER block
  132. Iterator childElems = sh.getChildElements();
  133. SOAPElement child = extractUserNameInfo(childElems);
  134. if (child != null) {
  135. // call method to perform authentication
  136. authenticated = authenticateRequest(child);
  137. }
  138. return authenticated;
  139. }
  140. private SOAPElement extractUserNameInfo(Iterator childElems) {
  141. SOAPElement child = null;
  142. Name sName;
  143. // iterate through child elements
  144. while (childElems.hasNext()) {
  145. Object elem = childElems.next();

  146. if (elem instanceof SOAPElement) {
  147. // Get child element and its name
  148. child = (SOAPElement) elem;
  149. sName = child.getElementName();
  150. // Check whether there is a UserNameToken element
  151. if (!USERNAME_TOKEN_STRING.equalsIgnoreCase(sName
  152. .getLocalName())) {
  153. if (child.getChildElements().hasNext()) { // TODO check
  154. logic
  155. return extractUserNameInfo(child.getChildElements());
  156. }
  157. }
  158. }
  159. }
  160. return child;
  161. }
  162. private boolean authenticateRequest(SOAPElement element) {
  163. boolean authenticated = false;
  164. // variable for user name and password
  165. String userName = null;
  166. String password = null;
  167. Name sName;
  168. // get an iterator on child elements of SOAP element
  169. Iterator childElems = element.getChildElements();
  170. SOAPElement child;
  171. // loop through child elements
  172. while (childElems.hasNext()) {
  173. // get next child element
  174. Object elem = childElems.next();
  175. if (elem instanceof SOAPElement) {
  176. child = (SOAPElement) elem;
  177. // get the name of SOAP element
  178. sName = child.getElementName();
  179. // get the value of username element
  180. if (USERNAME_STRING.equalsIgnoreCase(sName.getLocalName())) {
  181. userName = child.getValue();
  182. } else if (PASSWORD_STRING.equalsIgnoreCase(sName
  183. .getLocalName())) {
  184. // get the value of password element
  185. password = child.getValue();
  186. }
  187. if (userName != null && password != null) {
  188. authenticated = getUserAuth(userName, password);
  189. break;
  190. }
  191. }
  192. }
  193. if (userName == null || password == null) {
  194. log.warn("Username or password is empty. userName : [" + userName
  195. + "], password : [" + password + "]");
  196. }
  197. return authenticated;
  198. }
  199. public Map getConstantMap() {
  200. return constantMap;
  201. }
  202. public void setConstantMap(Map constantMap) {
  203. this.constantMap = constantMap;
  204. }
  205. private boolean getUserAuth(String username, String password) {
  206. //Constant Map populated with database information
  207. String dbUserId = (String) constantMap.get("useIdFormDatabase");
  208. String dbPassword = (String) constantMap
  209. .get("passwordFormDatabase");
  210. if (dbUserId.equalsIgnoreCase(username) && dbPassword.equals(password)) {
  211. return true;
  212. }
  213. return false;
  214. }
  215. }
4. HelloWorldManager.java —
  1. package com.vanrish.service;
  2. import com.vanrish.xml.schema.PeopleInfoRequest;
  3. import com.vanrish.xml.schema.PeopleInfoResponse;
  4. public interface HelloWorldManager {
  5. public PeopleInfoResponse getPeopleInfo(PeopleInfoRequest peopleInfoRequest) throws Exception;
  6. }
5. HelloWorldManagerImpl.java —
  1. package com.vanrish.service.impl;
  2. import java.math.BigDecimal;
  3. import java.math.BigInteger;
  4. import java.util.ArrayList;
  5. import java.util.Calendar;
  6. import java.util.Date;
  7. import java.util.GregorianCalendar;
  8. import java.util.Iterator;
  9. import java.util.List;
  10. import java.util.Map;
  11. import java.util.Set;
  12. import javax.xml.datatype.DatatypeConfigurationException;
  13. import javax.xml.datatype.DatatypeConstants;
  14. import javax.xml.datatype.DatatypeFactory;
  15. import javax.xml.datatype.XMLGregorianCalendar;
  16. import org.apache.commons.logging.Log;
  17. import org.apache.commons.logging.LogFactory;
  18. import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
  19. import com.vanrish.dao.UserDAO;
  20. import com.vanrish.model.PeopleVO;
  21. import com.vanrish.service.HelloWorldManager;
  22. import com.vanrish.xml.schema.ObjectFactory;
  23. import com.vanrish.xml.schema.Person;
  24. import com.vanrish.xml.schema.PeopleInfoRequest;
  25. import com.vanrish.xml.schema.PeopleInfoResponse;
  26. public class HelloWorldManagerImpl implements HelloWorldManager {
  27. private UserDAO userDAO;
  28. public PeopleInfoResponse getPeopleInfo(PeopleInfoRequest peopleInfoRequest) throws Exception {
  29. ObjectFactory factory = new ObjectFactory();
  30. PeopleInfoResponse peopleInfoResponse = factory.createPeopleInfoResponse();
  31. PeopleVO peopleVO = new PeopleVO();
  32. peopleVO.setPeopleId(peopleInfoRequest.getPeopleId());
  33. peopleVO = userDAO.getPeopleInfo(peopleVO);
  34. Person person = factory.createPerson();
  35. person.setFirstName(peopleVO.getFirstName());
  36. person.setLastName(peopleVO.getLastName());
  37. person.setType(peopleVO.getPeopleType());
  38. person.setCreateDate(getXmlDate(peopleVO.getCreateDate()));
  39. peopleInfoResponse.setPerson(person);
  40. peopleInfoResponse.setMessage(SUCCESS_MESSAGE);
  41. peopleInfoResponse.setSuccess(true);
  42. return peopleInfoResponse;
  43. }
  44. private XMLGregorianCalendar getXmlDate(Date date) {
  45. try {
  46. GregorianCalendar cal = new GregorianCalendar();
  47. cal.setTime(date);
  48. XMLGregorianCalendar gc = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
  49. gc.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
  50. gc.setTime(DatatypeConstants.FIELD_UNDEFINED,
  51. DatatypeConstants.FIELD_UNDEFINED,
  52. DatatypeConstants.FIELD_UNDEFINED);
  53. return gc;
  54. } catch (DatatypeConfigurationException e) {
  55. log.warn("Cannot format expxiration date: " + date);
  56. return null;
  57. }catch(Exception ex){
  58. log.warn("Cannot format expxiration date: " + ex);
  59. return null;
  60. }
  61. }
  62. public void setUserDAO(UserDAO userDAO) {
  63. this.userDAO = userDAO;
  64. }
  65. }