Client Side Authentication in HTTP Gateway

Version 1

    Dear All,

    I had to do this for my project. If any one of you have this requirement then you can use it.

     

    Environment Details :-

    Consumer on HTTPs with client auth enabled

    web server (apache or can be anything)

    app server (Jboss ESB +++++) [I was using SOA-P 5.2]

     

    Issue :-

    httpgateway (basically a listener) can't get client cert to authenticate the details of the customer.

     

    Descritption, short coming and Solution :-

    Basically problem lies in the layer where HTTP request with all it's header is passed to the action chain in the service where typicaly your gatewaty is httpgateway.

     

    <http-gateway name="HTTP-Gateway".....

    Internally it uses few classes. Of particular interest to us are two classes which is used and needs to be customized to meet the need.

    org.jboss.soa.esb.listeners.gateway.http.HttpMessageComposer

    org.jboss.soa.esb.listeners.gateway.http.HttpRequestWrapper

     

     

    Changes that you will need to are following :-

    [1] Instruct ESB service to use your custom message composer class

    [2] Create a custom message composer class

    [3] Create a custom HttpRequestWraper class which can handle attributes, http headers and locales (what ever you need is, not necessary to handle everything)

     

    Steps in details

    [1] Instruct ESB service to use your custom message composer class

     

    Modify your jboss-esb.xml file and add below statement as part of <http-gateway> tag

     

    <property name="composer-class" value="esb.gateway.CustomHttpMessageComposer"/>

    This tell the service to use the customized HttpMessageComposer

     

    [2] Create a custom message composer class

     

    package esb.gateway;
    import java.util.Enumeration;
    import java.util.List;

    import javax.servlet.http.HttpServletRequest;

    import org.apache.log4j.Logger;
    import org.jboss.soa.esb.http.HttpHeader;
    import org.jboss.soa.esb.http.HttpRequest;
    import org.jboss.soa.esb.listeners.gateway.http.HttpMessageComposer;
    import org.jboss.soa.esb.listeners.gateway.http.HttpRequestWrapper;

    public class CustomHttpMessageComposer<T extends HttpRequestWrapper> extends HttpMessageComposer<T>{

    private static final Logger logger = Logger.getLogger(CustomHttpMessageComposer.class);

        @Override

    /*

    * this is the only method you need to override.

    * make it return your custom httprequest class which can handle attributes and headers and locale

    * retain all the original functionality by calling the super class method

    */
        public CustomHttpRequest getRequestInfo(HttpServletRequest request) {
         logger.info("co.za.fnbwealth.esb.gateway.CustomHttpMessageComposer getRequestInfo() called");
            HttpRequest requestInfo = super.getRequestInfo(request);
            CustomHttpRequest customRequestInfo = new CustomHttpRequest(requestInfo);
           
            //Copy the attributes. This will have cert info
            Enumeration attrNames = request.getAttributeNames();
      while(attrNames.hasMoreElements()){
       String attrName = (String)attrNames.nextElement();
       Object val = request.getAttribute(attrName);
       customRequestInfo.setAttribute(attrName, val);
      }
     
      //Copy Headers
      Enumeration hdrNames = request.getHeaderNames();
      List<HttpHeader> hdrs = customRequestInfo.getHeaders();
      while(hdrNames.hasMoreElements()){
       String hdrName = (String)hdrNames.nextElement();
       String hdrVal = request.getHeader(hdrName);
       HttpHeader hdr = new HttpHeader(hdrName, hdrVal);
       hdrs.add(hdr);
      }
     
      //Copy Locale
      customRequestInfo.setLocales(request.getLocales());

            return customRequestInfo;
        }

    }

     

    [3] Create a custom HttpRequestWraper class

    package esb.gateway;

    import java.io.Serializable;
    import java.security.cert.X509Certificate;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.Map;

    import org.apache.log4j.Logger;
    import org.jboss.soa.esb.http.HttpRequest;

    public class CustomHttpRequest extends HttpRequest implements Serializable{
    private static final Logger logger = Logger.getLogger(CustomHttpRequest.class);

    private X509Certificate clntCerts[];
    private Map<String, Object> attrs = new HashMap<String, Object>();
    //Locales implementation hasn't been tested including getter and setter for locales
    private Enumeration locales;
    /*

    * In the costructor get all the values by making call to the super class

    * Add getter and setter method for nea features you want

    * Here methods have been added to handle attribute(cleint cert comes in this), http header and locale

    */
    public CustomHttpRequest(HttpRequest parent){
      logger.info("co.za.fnbwealth.esb.gateway.CustomHttpRequest constructor called");
      super.setRemoteAddr(parent.getRemoteAddr());
      super.setAuthType(parent.getAuthType());
      super.setCharacterEncoding(parent.getCharacterEncoding());
      super.setContentType(parent.getContentType());
         super.setContextPath(parent.getContextPath());
         super.setLocalAddr(parent.getLocalAddr());
         super.setLocalName(parent.getLocalName());
         super.setMethod(parent.getMethod());
         super.setPathInfo(parent.getPathInfo());
         super.setProtocol(parent.getProtocol());
         super.setQueryString(parent.getQueryString());
         super.setRemoteAddr(parent.getRemoteAddr());
         super.setRemoteHost(parent.getRemoteHost());
         super.setRemoteUser(parent.getRemoteUser());
         super.setContentLength(parent.getContentLength());
         super.setRequestSessionId(parent.getRequestSessionId());
         super.setRequestURI(parent.getRequestURI());
         super.setScheme(parent.getScheme());
         super.setServerName(parent.getServerName());
         super.setRequestPath(parent.getRequestPath());
         super.setPathInfo(parent.getPathInfo());
    }
    public Object getAttribute(String attrName){
      logger.info("co.za.fnbwealth.esb.gateway.CustomHttpRequest getAttribute("+attrName+") called");
      return attrs.get(attrName);
    }

    public void setAttribute(String attrName, Object value){
      logger.info("co.za.fnbwealth.esb.gateway.CustomHttpRequest setAttribute("+attrName+", "+value+") called");
      attrs.put(attrName, value);
    }

    public void removeAttributeNames(String attrName){
      logger.info("co.za.fnbwealth.esb.gateway.CustomHttpRequest removeAttributeNames("+attrName+") called");
      attrs.remove(attrName);
    }

    public Map<String, Object> getAttributes(){
      logger.info("co.za.fnbwealth.esb.gateway.CustomHttpRequest getAttributes() called");
      return attrs;
    }

    public Enumeration getLocales(){
      return locales;
    }

    public void setLocales(Enumeration locales){
      this.locales = locales;
    }
    }

     

    This is all that's required for this.

     

    Finally note that you need to get and use customHttpRequest you have created in your custom action where you want to inspect the client cert(or. anything you customized).

     

    CustomHttpRequest req = (CustomHttpRequest)message.getProperties().getProperty(

    "org.jboss.soa.esb.http.HttpRequest#request"

    );

     

    Good Luck !!!

    Cheers,

    -Rajnish