divendres, 8 de juliol del 2011

Adding handlers to Spring web services

In my previous related entry I showed how easy it was to create a web service using Spring-WS and annotations. Now, we're discussing how to add a handler to the web service. In particular, we will add a handler that will extract the user requesting the service from a SAML token attached to it.

To add a handler to oour existing web service, we just need to files:
  • the handler implementation
  • an XML file (aka bingings file) defining the handler chain for the web service
Finally, we will just need to wire them.

Creating the handler implementation

To implement the hanlder, we just need to create a class that implements the interface javax.xml.ws.handler. This interface will force us to implement the following methods:

  • handleMessage
  • handleFault
  • close
  • getHeaders
Since in this sample we just want to validate the user requesting (extracting it from a SAML token) our service, we're only implementing the handleMessage method:

package com.blogspot.aigloss.springws.handler;

import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.soap.SOAPFaultException;

import org.opensaml.common.xml.SAMLConstants;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class SAMLHandler implements SOAPHandler {
 private static String WS_SECURITY_URI =
   "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";

 public boolean handleMessage(SOAPMessageContext smc) {
   SOAPFaultException exception = null;
   Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
   if (!outboundProperty.booleanValue()) {
     Element assertionElement;
     try {
       // get SOAP Header
       SOAPHeader sh = smc.getMessage().getSOAPHeader();
       if (sh == null) {
         // throw SOAPFaultException
       }
       // check for wsse:security element under SOAP Header
       Node wsse = sh.getFirstChild();
       if (wsse == null || !"Security".equals(wsse.getLocalName())
           || !WS_SECURITY_URI.equals(wsse.getNamespaceURI())) {
         //throw SOAPFaultException exception
       }

       // check for SAML assertion under wsse:security element
       assertionElement = (Element) wsse.getFirstChild();
       if (assertionElement == null
           || !"Assertion".equals(assertionElement.getLocalName())
           || !SAMLConstants.SAML20_NS.equals(assertionElement.getNamespaceURI())) {
         //throw SOAPFaultException exception
       }
       String user = null;
       //DON'T USE DOM! USE XPATH INSTEAD!!
       NodeList nodeList = assertionElement.getElementsByTagName("saml:NameID");
       if(nodeList.getLength() == 1) {
         user = nodeList.item(0).getTextContent();
       } else {
         //NameID not found under assertion token --> throw SOAPFaultException
       }
       if(exception == null) {
         //validate user
       }
     } catch (Exception e) {
       //throw SOAPFaultException exception
     }
     if(exception != null) {
       throw exception;
     }
   }
   return true;
 }

 public boolean handleFault(SOAPMessageContext context) {
   return false;
 }

 public void close(MessageContext context) {
 }

 public Set getHeaders() {
   return null;
 }
}


Note I've retrieved the NameId element content form the SAML token using DOM (code in red). I've done it just to tell you this is not the best way to retrieve it. Instead, use Xpath to get it.

Creating the bindings file


To inform the web service that there is one or more handler who want to manage the request, we need to create a handler chain. A handler chain is just a sequence of handlers that need to be applied in an specific order. It is defined in an XML file like this:


<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
   <handler-chain>
      <handler>
       <handler-name>SAMLHandler</handler-name>
         <handler-class>com.blogspot.aigloss.ws.handler.SAML2Handler</handler-class>
      </handler>
   </handler-chain>
</handler-chains>


Linking the bindings file with the handler implementation

Right now we have the implementation of our handler, and the definition on our handler chain.  So, finally, we just have to wire one with the other so that it all makes sense (and, of course, get it to work!). To get that wire, we're annotating the service endpoint class with javax.jws.HandlerChain:

@HandlerChain(file="myservice_bindings.xml")

Since we haven't specified any path for the file, we'll put it in the same directory as the service endpoint. For example, if you're working with Maven, you could put it into the same path as your service endpoint implementation but, instead of having it under /src/main/java, under /src/main/resources.

5 comentaris:

  1. Un administrador del blog ha eliminat aquest comentari.

    ResponElimina
  2. L'autor ha eliminat aquest comentari.

    ResponElimina
  3. Great job for publishing such a beneficial web site. Your web log isn’t only useful but it is additionally really creative too. There tend to be not many people who can certainly write not so simple posts that artistically. Continue the nice writing
    video and blog marketing

    ResponElimina