Buscar este blog

Mostrando entradas con la etiqueta Servlet. Mostrar todas las entradas
Mostrando entradas con la etiqueta Servlet. Mostrar todas las entradas

martes, 27 de marzo de 2018

X509 Certificate Mock Servlet Filter

This is a way low-technolgy-solution to simulate, in web application, an SSL mutual authentication.  The obvious solution would be to configure Apache httpd as front-end, and let it to ask for the user certificate, but I had some problems with an android web browser...
So, instead configure Apache with a "SSLVerifyClient require" this is my ashamed solution: to configure a servlet filter (at first position in the chain) and, if there is no certificate in the request, just put one and go on.
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class X509CertificateFilter implements Filter {
    private static final Logger LOGGER = LoggerFactory.getLogger(X509CertificateFilter.class);
    
    private static final String CERTIFICATE = "-----BEGIN CERTIFICATE-----\r\n" + 
            "MIIIdDCCB1ygAwIBAgIQIDbcJ3psq8FXD6IQBZA15DANBgkqhkiG9w0BAQsFADBN\r\n" +             
            "(...)\r\n" + 
            "740qIwTZlA4=\r\n" + 
            "-----END CERTIFICATE-----";
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {                
    }

    @Override
    public void destroy() {        
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        if (request.getAttribute("javax.servlet.request.X509Certificate") == null) {
            request.setAttribute("javax.servlet.request.X509Certificate", buildCertificates());
        }
        
        chain.doFilter(request, response);    
    }

    private static X509Certificate[] buildCertificates() {
        try {        
            final InputStream is = new ByteArrayInputStream(CERTIFICATE.getBytes());                    
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
            return new X509Certificate[] {cert};
        }
        catch(Exception e) {
            LOGGER.error("Error injecting certificate", e);
            return null;
        }
    }    
}

You can also configure multiple certificates, for example in a map, and use a request parameter to pick one...

viernes, 25 de marzo de 2016

Java servlet download log file

This is a basic servlet to download the log files of  a web app.
The servlet has the following config params:
  • logsConfigLocation. The base dir in which the log files are stored. It is not allowed to read files outside this directory.
  • logsMaxSize. The maximum size of the log file allowed to download
The config params can be configured usen Spring Expression Language, so a valid value could be ${my.config.property:/home/jboss/logs}
If none is specified, the default values are jboss.server.log.dir and 10MB respectively.


In order to prevent public access to this files, the servlet can be configured to use basic authentication. In this case, you need to configure the appropriate login mechanism in the app, form example, in web.xml:
<login-config>
 <auth-method>BASIC</auth-method>
 <realm-name>default</realm-name>
</login-config>

The servlet receives a request param called "path" which contains the relative route of the requested log file.

LoggerServlet:
package es.sisifo.jee6.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.HttpConstraint;
import javax.servlet.annotation.ServletSecurity;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.util.ServletContextPropertyUtils;


@WebServlet(urlPatterns = { "/loggerServlet" })
@ServletSecurity(@HttpConstraint(rolesAllowed = { "admin" }))
public class LoggerServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public static final String CONFIG_LOCATION_PARAM = "logsConfigLocation";
    public static final String CONFIG_MAXSIXE_PARAM = "logsMaxSize";

    private String logsLocation = System.getProperty("jboss.server.log.dir");
    private int maxFileSize = 10485760; // 10MB

    @Override
    public void init(final ServletConfig servletConfig) throws ServletException {
        super.init(servletConfig);

        if (servletConfig.getInitParameter(CONFIG_LOCATION_PARAM) != null) {
            logsLocation = ServletContextPropertyUtils.resolvePlaceholders(
                    servletConfig.getInitParameter(CONFIG_LOCATION_PARAM), servletConfig.getServletContext());
        }

        if (servletConfig.getInitParameter(CONFIG_MAXSIXE_PARAM) != null) {
            maxFileSize = Integer.valueOf(ServletContextPropertyUtils.resolvePlaceholders(
                    servletConfig.getInitParameter(CONFIG_MAXSIXE_PARAM), servletConfig.getServletContext()));
        }
    }


    @Override
    protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException,
            IOException {

        final String path = request.getParameter("path");
        if (!isValidPath(path)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid path");
            return;
        }

        final File logFile = new File(logsLocation + File.separator + path);
        if (!isValidFile(logFile)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid file");
            return;
        }

        downloadFile(logFile, response);
    }


    private void downloadFile(final File logFile, final HttpServletResponse response) throws IOException {
        final FileInputStream inStream = new FileInputStream(logFile);

        response.setContentType(getServletContext().getMimeType(logFile.getAbsolutePath()));
        response.setContentLength((int) logFile.length());
        response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", logFile.getName()));


        final OutputStream outStream = response.getOutputStream();

        final byte[] buffer = new byte[4096];
        int bytesRead = -1;
        while ((bytesRead = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, bytesRead);
        }

        inStream.close();
        outStream.close();
    }


    private boolean isValidPath(final String path) {
        return path != null && !path.contains("..");
    }


    private boolean isValidFile(final File logFile) {
        return logFile.exists() && logFile.isFile() && logFile.length() <= maxFileSize;
    }
}

domingo, 21 de febrero de 2016

XMLHttpRequest - Download PDF and show it in iframe

These snippets show how to download a PDF from a Servlet and how to show it inside a iframe.
The immediate solution would be to invoke the servlet by GET, and to put this URL in de src attribute of the iframe, but in this case I want to invoke the servlet by POST.

Servlet code:
@WebServlet(urlPatterns="/sello")
public class SelloRegistroServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;

 private final SelloService selloService = new SelloServiceImpl();

 @Override
 protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
     final DatosSello datos = recuperarDatosSello(request);
     final ConfiguracionSello config = recuperarDatosConfig(request);

     final ByteArrayOutputStream baos = selloService.generarSello(datos, config);
  
            response.setContentType("application/pdf");
            response.setContentLength(baos.size());
            response.setHeader("Content-Disposition", "inline;filename=\"selo.pdf\"");
            response.setHeader("filename", "selo.pdf");

            final OutputStream outStream = response.getOutputStream();
            outStream.write(baos.toByteArray());
            outStream.flush();
            outStream.close();
 }
 
 private DatosSello recuperarDatosSello(final HttpServletRequest request) {
     (...)
 }


 private ConfiguracionSello recuperarDatosConfig(final HttpServletRequest request) {
     (...)
 }
}

HTML code:
<html>
<head>          
    <script type="text/javascript">
        var urlSello = 'http://localhost:8080/registro-itext/sello';
        
        var generarSello = function() {          
            var data = {
                    numeroRegistro:             document.getElementsByName("numReg")[0].value,
                    fechaRegistro:              document.getElementsByName("fechaReg")[0].value,
                    codigoOficina:              document.getElementsByName("codigoOfi")[0].value,
                    nombreOficina:              document.getElementsByName("nombreOfi")[0].value,
                    codigoUnidad:               document.getElementsByName("codigoUnidad")[0].value,
                    nombreOficina:              document.getElementsByName("nombreOfi")[0].value
            };
            
            var params = Object.keys(data).map(function(key) {
                return key + '=' +  encodeURI(data[key]);
            }).join('&');
            
            var xhr = new XMLHttpRequest();           
            xhr.onreadystatechange = function(){                
                if (this.readyState == 4 && this.status == 200){
                    console.log(this.response, typeof this.response);               
                    var urlBuilder = window.URL || window.webkitURL;
                    document.getElementById("iFramePDF").src = urlBuilder.createObjectURL(this.response);
                    document.getElementById("iFramePDF").style.display = 'block';               
                }
            };    
            xhr.open('POST', urlSello, true);
            xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            xhr.setRequestHeader("Content-length", params.length);
            xhr.responseType = 'blob';
            xhr.send(params);                       
        };                
    </script>
</head>

<body>
    <div id="contenido">     
        <button id="btnSello" type="button" onclick="generarSello();">Generar Sello</button>
                  
        <table>
            <tr><td>Numero de registro: </td> <td> <input name="numReg"  type="text" value="899998"></td></tr>
            <tr><td>Fecha de registro</td> <td> <input name="fechaReg"   type="text" value="2015/01/01 10:10:00"></td></tr>
            <tr><td>Codigo de oficina</td> <td> <input name="codigoOfi"  type="text" value="000"></td></tr>
            <tr><td>Nombre de oficina</td> <td> <input name="nombreOfi"  type="text" value="Rexistro Xeral \nProbas"></td></tr>
            <tr><td>Codigo de unidad</td> <td> <input name="codigoUnidad" type="text" value="V0"></td></tr>           
        </table>
    </div>
    

    <iframe id="iFramePDF" name="iFramePDF" src=""  width="100%" height="100%" frameBorder="0" styele="display:none"></iframe>

</body>
</html>

The important parts are:
  1. You create an object with all the params to serialize it in the way "name=value&" and to send the outcome string to the servlet.
  2. You invoke the servlet by using XMLHttpRequest. In the callback function, you convert the whole response to a new URL and pass it to the iframe