Monday, January 19, 2015

SSL Interceptor for Struts2

Works with SSLInterceptor plugin for Struts2
Configuration in the struts.xml
 
RequestUtil.java
package mycom.myapp.ssl;

import javax.servlet.http.HttpServletRequest;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;


public class RequestUtil {
    public static String buildQueryString(HttpServletRequest request) {
        // add query string, if any
        String queryString = request.getQueryString();
        StringBuffer finalQs = new StringBuffer();

        if (queryString != null && queryString.length() != 0) {
            finalQs.append(queryString);
        } else {
            queryString = RequestUtil.getRequestParameters(request);
            if (queryString != null && queryString.length() != 0) {
                finalQs.append(queryString);
            }
        }

        return finalQs.length()== 0 ? null : finalQs.toString();

    }


    public static String getRequestParameters(HttpServletRequest aRequest) {

        return createQueryStringFromMap(aRequest.getParameterMap(), "&", aRequest).toString();
    }


    public static StringBuffer createQueryStringFromMap(Map m, String ampersand, HttpServletRequest req) {
        StringBuffer aReturn = new StringBuffer("");
        Set aEntryS = m.entrySet();
        Iterator aEntryI = aEntryS.iterator();
        while (aEntryI.hasNext()) {
            Map.Entry aEntry = (Map.Entry) aEntryI.next();
            Object value = aEntry.getValue();
            String[] aValues = new String[1];
            if (value == null) {
                aValues[0] = "";
            } else if (value instanceof List) { // Work around for Weblogic 6.1sp1
                List aList = (List) value;
                aValues = (String[]) aList.toArray(new String[aList.size()]);
            } else if (value instanceof String) {  // Single value from Struts tags
                aValues[0] = (String) value;
            } else { // String array, the standard returned from request.getParameterMap()
                aValues = (String[]) value;  // This is the standard
            }
            for (int i = 0; i < aValues.length; i++) {

                append(aEntry.getKey(), aValues[i], aReturn, ampersand, req);
            }
        }
        return aReturn;
    }

    private static StringBuffer append(Object key, Object value, StringBuffer queryString, String ampersand, HttpServletRequest req) {

        if (queryString.length() > 0) {
            queryString.append(ampersand);
        }

        // NTS: remove URLEncoder - causes symbols to be stored as hex values
        queryString.append(key.toString());
        queryString.append("=");
        queryString.append(value.toString());

        return queryString;
    }


}

Secured.java

package mycom.myapp.ssl;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
//@Target(METHOD)
public @interface Secured { }

SSLInterceptor.java
package mycom.myapp.ssl;
//http://code.google.com/p/struts2-ssl-plugin/wiki/HowToUse
//Java API imports
import java.lang.reflect.Method;
import java.net.URI;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.apache.struts2.StrutsStatics;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

//Commons API imports
//Struts API imports
public class SSLInterceptor extends AbstractInterceptor {
 private static final long serialVersionUID = 1L;
 private static final Logger logger = Logger.getLogger(AjaxInterceptor.class); 

  private String httpsPort;
  private String httpPort;
  private boolean useAnnotations = true;

  /**
   * Defaults for HTTP and HTTPS ports.  Can be overridden in as a interceptor parm in config file.
   */
  final static int HTTP_PORT = 8080;
  final static int HTTPS_PORT = 8443;

  final static String HTTP_GET = "GET";
  final static String HTTP_POST = "POST";
  final static String SCHEME_HTTP = "http";
  final static String SCHEME_HTTPS = "https";

  /** Creates a new instance of SSLInterceptor */
  public SSLInterceptor() {
      super();
      logger.info ("Intializing SSLInterceptor");
  }

  /**
   * Redirect to SSL or non-SSL version of page as indicated by the presence (or absence) of the
   *  @Secure annotation on the action class.
   */
  public String intercept(ActionInvocation invocation) throws Exception {

      // initialize request and response
      final ActionContext context = invocation.getInvocationContext ();
      final HttpServletRequest request =
          (HttpServletRequest) context.get(StrutsStatics.HTTP_REQUEST);
      final HttpServletResponse response =
          (HttpServletResponse) context.get(StrutsStatics.HTTP_RESPONSE);

      //  add bypass for file uploads
      if (isFileUploadRequest(request)) {
       return invocation.invoke();
      }
      
      //add by pass for session tokens
      String queryString = RequestUtil.buildQueryString(request);
      if (queryString != null && (queryString.toLowerCase().indexOf("&token=") != -1 || queryString.toLowerCase().indexOf("token=") != -1)) {
       return invocation.invoke();
      }
      
      // check scheme
      String scheme = request.getScheme().toLowerCase();

      // check method
      String method = request.getMethod().toUpperCase();


      // if useAnnotations is true check for the annotaion marker in the class level or method level
      // else make every request secure.
      // If the action class/method uses the Secured marker annotation, then see if we need to
      // redirect to the SSL protected version of this page

      Object action = invocation.getAction();
      Method method2 = getActionMethod(action.getClass(), invocation.getProxy().getMethod());

      boolean flg=false;
      
      if (request.getSession().getAttribute("requestUri") != null && request.getRequestURI().equals((String)request.getSession().getAttribute("requestUri"))) {
       //we are coming for 2nd time
    request.getSession().setAttribute("requestUri", null);
    flg=true;
   } else {
    request.getSession().setAttribute("requestUri", request.getRequestURI());
    
   }
      if (request.getSession().getAttribute("switchToHttp") != null) {
       request.getSession().setAttribute("switchToHttp", null);
       return invocation.invoke();
      }
      // If the protocols are the same allow to pass without a redirect
      // NOTE: if statements are seperated but doesn't need to be
      //       think it might be easier to read as separate if else statements
      // If https
      // Else if http
      if ((action.getClass().isAnnotationPresent(Secured.class) ||
           method2.isAnnotationPresent(Secured.class) &&
           referer.toLowerCase().startsWith(SCHEME_HTTPS.toLowerCase()))) {
       return invocation.invoke();
      }
      else if (!method2.isAnnotationPresent(Secured.class) &&
               referer.toLowerCase().startsWith(SCHEME_HTTP.toLowerCase() + "://")) {
       return invocation.invoke();
      }
      
      if ( flg ) {
       //we are here 2nd time with http scheme
          if ( (HTTP_GET.equals(method) || HTTP_POST.equals(method)) && SCHEME_HTTP.equals(scheme)){

           if ((!isUseAnnotations() || action.getClass().isAnnotationPresent(Secured.class) || method2.isAnnotationPresent(Secured.class) )) {
            return invocation.invoke();
           }
              // initialize https port
              int httpsPort = getHttpPort() == null? HTTP_PORT : Integer.parseInt(getHttpPort());

              URI uri = new URI(SCHEME_HTTP, null, request.getServerName(),
                  httpsPort, response.encodeRedirectURL(request.getRequestURI()),
                  queryString, null);

              logger.info("Going to SSL mode , redirecting to " + uri.toString());

              response.sendRedirect(uri.toString());
              return null;
          }

      } else if (!isUseAnnotations() || action.getClass().isAnnotationPresent(Secured.class) || method2.isAnnotationPresent(Secured.class) ){
      
       
          if ( (HTTP_GET.equals(method) || HTTP_POST.equals(method)) && SCHEME_HTTP.equals(scheme)){

              // initialize https port
              int httpsPort = getHttpsPort() == null? HTTPS_PORT : Integer.parseInt(getHttpsPort());

              URI uri = new URI(SCHEME_HTTPS, null, request.getServerName(),
                  httpsPort, response.encodeRedirectURL(request.getRequestURI()),
                  queryString, null);

              logger.info("Going to SSL mode, redirecting to " + uri.toString());

              response.sendRedirect(uri.toString());
              return null;
          }
      }  else{

          if ((HTTP_GET.equals(method) || HTTP_POST.equals(method)) && SCHEME_HTTP.equals(scheme)){//used to be SCHEME_HTTPS

              // initialize http port
              int httpPort = getHttpPort() == null? HTTP_PORT : Integer.parseInt(getHttpPort());

              URI uri = new URI(SCHEME_HTTP, null, request.getServerName(),
                  httpPort, response.encodeRedirectURL(request.getRequestURI()),
                  queryString, null);

              logger.info("Going to non-SSL mode, redirecting to " + uri.toString());
              request.getSession().setAttribute("switchToHttp", "true");
              response.sendRedirect(uri.toString());
              return null;
          }
          
      }

      return invocation.invoke();
  }
  
  // NTS
  private boolean isFileUploadRequest(HttpServletRequest request) {
   return request.getContentType() != null && request.getContentType().toLowerCase().startsWith("multipart/form-data") 
     && request.getMethod() != null && request.getMethod().equalsIgnoreCase("POST");
  }
  
  // FIXME: This is copied from DefaultActionInvocation but should be exposed through the interface
  protected Method getActionMethod(Class actionClass, String methodName) throws NoSuchMethodException {
      Method method;
      try {
          method = actionClass.getMethod(methodName, new Class[0]);
      } catch (NoSuchMethodException e) {
          // hmm -- OK, try doXxx instead
          try {
              String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
              method = actionClass.getMethod(altMethodName, new Class[0]);
          } catch (NoSuchMethodException e1) {
              // throw the original one
              throw e;
          }
      }
      return method;
  }


  public String getHttpsPort() {
      return httpsPort;
  }

  @Inject(value="struts2.sslplugin.httpsPort",required = false)
  public void setHttpsPort(String httpsPort) {
      this.httpsPort = httpsPort;
  }

  public String getHttpPort() {
      return httpPort;
  }

  @Inject(value = "struts2.sslplugin.httpPort", required = false)
  public void setHttpPort(String httpPort) {
      this.httpPort = httpPort;
  }

  public boolean isUseAnnotations() {
      return useAnnotations;
  }

  public void setUseAnnotations(boolean useAnnotations) {
      this.useAnnotations = useAnnotations;
  }

  @Inject(value = "struts2.sslplugin.annotations", required = false)
  public void setAnnotations(String annotations) {
      if (annotations==null) {
          annotations = "true";
      }
      this.useAnnotations = new Boolean(annotations).booleanValue();
  }

}

No comments:

Post a Comment