springsecurityfilterchain delegatingfilterproxy java tomcat6 servlet-filters

java - delegatingfilterproxy - spring filter



Buscando un ejemplo para insertar contenido en la respuesta usando un filtro servlet (3)

La base de código que estoy usando llama al método getOutputStream, en lugar de getWriter cuando procesa la respuesta, por lo que los ejemplos incluidos en la otra respuesta no ayudan. Aquí hay una respuesta más completa que funciona tanto con OutputStream como con PrintWriter, incluso con errores correctos, si se accede al escritor dos veces. Esto se deriva del gran ejemplo, PETICIÓN DE VOLQUETE Y RESPUESTA CON JAVAX.SERVLET.FILTER .

import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class MyFilter implements Filter { private FilterConfig filterConfig = null; private static class ByteArrayServletStream extends ServletOutputStream { ByteArrayOutputStream baos; ByteArrayServletStream(ByteArrayOutputStream baos) { this.baos = baos; } public void write(int param) throws IOException { baos.write(param); } } private static class ByteArrayPrintWriter { private ByteArrayOutputStream baos = new ByteArrayOutputStream(); private PrintWriter pw = new PrintWriter(baos); private ServletOutputStream sos = new ByteArrayServletStream(baos); public PrintWriter getWriter() { return pw; } public ServletOutputStream getStream() { return sos; } byte[] toByteArray() { return baos.toByteArray(); } } public class CharResponseWrapper extends HttpServletResponseWrapper { private ByteArrayPrintWriter output; private boolean usingWriter; public CharResponseWrapper(HttpServletResponse response) { super(response); usingWriter = false; output = new ByteArrayPrintWriter(); } public byte[] getByteArray() { return output.toByteArray(); } @Override public ServletOutputStream getOutputStream() throws IOException { // will error out, if in use if (usingWriter) { super.getOutputStream(); } usingWriter = true; return output.getStream(); } @Override public PrintWriter getWriter() throws IOException { // will error out, if in use if (usingWriter) { super.getWriter(); } usingWriter = true; return output.getWriter(); } public String toString() { return output.toString(); } } public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } public void destroy() { filterConfig = null; } public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { CharResponseWrapper wrappedResponse = new CharResponseWrapper( (HttpServletResponse)response); chain.doFilter(request, wrappedResponse); byte[] bytes = wrappedResponse.getByteArray(); if (wrappedResponse.getContentType().contains("text/html")) { String out = new String(bytes); // DO YOUR REPLACEMENTS HERE out = out.replace("</head>", "WTF</head>"); response.getOutputStream().write(out.getBytes()); } else { response.getOutputStream().write(bytes); } } }

He estado buscando en la red y en stackoverflow un ejemplo de alguien que inserta contenido en la respuesta usando un filtro de servlet, pero solo puedo encontrar ejemplos de gente que captura / comprime la salida y / o cambia los encabezados. Mi objetivo es agregar un fragmento de HTML justo antes del cierre </ body> de todas las respuestas HTML.

Estoy trabajando en una solución que amplía el HttpServletResponseWrapper para usar mi propio PrintWriter, y luego anula los métodos de escritura al respecto. Dentro del método de escritura estoy almacenando los últimos 7 caracteres para ver si es igual a la etiqueta de cuerpo de cierre, y luego escribo mi fragmento de HTML más la etiqueta de cuerpo de cierre, antes de continuar con las operaciones de escritura normales para el resto del documento.

Siento que alguien ya debe haber resuelto este problema, y ​​probablemente más elegantemente que yo. Agradecería cualquier ejemplo de cómo usar un filtro de servlet para insertar contenido en una respuesta.

ACTUALIZADO

Respondiendo a un comentario, también estoy tratando de implementar CharResponseWrapper desde http://www.oracle.com/technetwork/java/filters-137243.html . Aquí está mi código:

PrintWriter out = response.getWriter(); CharResponseWrapper wrappedResponse = new CharResponseWrapper( (HttpServletResponse)response); chain.doFilter(wrappedRequest, wrappedResponse); String s = wrappedResponse.toString(); if (wrappedResponse.getContentType().equals("text/html") && StringUtils.isNotBlank(s)) { CharArrayWriter caw = new CharArrayWriter(); caw.write(s.substring(0, s.indexOf("</body>") - 1)); caw.write("WTF</body></html>"); response.setContentLength(caw.toString().length()); out.write(caw.toString()); } else { out.write(wrappedResponse.toString()); } out.close();

También estoy completando la solicitud, pero ese código funciona y no debería afectar la respuesta.


La respuesta iTech funcionó parcialmente para mí y esto se basa en esa respuesta.

Pero debe tener en cuenta que parece que algunos servidores web (y AppEngine Standard) cierran el outputStream después de la primera llamada a chain.doFilter dentro de un filtro.

Entonces, cuando necesite escribir en PrintWritter previamente guardado, la transmisión se cerrará y obtendrá una pantalla en blanco. (No recibí ni siquiera un error para darme cuenta de lo que estaba sucediendo).

Así que la solución para mí fue crear un ServletOutputStream "ficticio" y volver al método getOutputStream de mi ResponseWrapper.

Estos cambios más la solución de iTech me permitieron insertar una respuesta jsp completamente procesada en html dentro de una respuesta json (escapando adecuadamente caracteres conflictivos como comillas).

Este es mi código:

Myfilter

@WebFilter({"/json/*"}) public class Myfilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //Save original writer PrintWriter out = response.getWriter(); //Generate a response wrapper with a different output stream ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response); //Process all in the chain (=get the jsp response..) chain.doFilter(request, responseWrapper); //Parse the response out.write("BEFORE"+responseWrapper.toString()+"AFTER"); //Just + for clear display, better use a StringUtils.concat } @Override public void destroy() {} }

Mi ResponseWrapper :

public class ResponseWrapper extends HttpServletResponseWrapper { private StringWriter output; public String toString() { return output.toString(); } public ResponseWrapper(HttpServletResponse response) { super(response); //This creates a new writer to prevent the old one to be closed output = new StringWriter(); } public PrintWriter getWriter() { return new PrintWriter(output,false); } @Override public ServletOutputStream getOutputStream() throws IOException { //This is the magic to prevent closing stream, create a "virtual" stream that does nothing.. return new ServletOutputStream() { @Override public void write(int b) throws IOException {} @Override public void setWriteListener(WriteListener writeListener) {} @Override public boolean isReady() { return true; } }; } }


Tendrá que implementar HttpServletResponseWrapper para modificar la respuesta. Vea este documento http://www.oracle.com/technetwork/java/filters-137243.html , tiene un ejemplo que transforma la respuesta, que es más de lo que desea

Editar

Probé un Servlet simple con filtro de respuesta y funcionó perfectamente. El Servlet saca la Test cadena y el filtro de respuesta anexa la cadena filtered y finalmente, cuando corro desde el navegador, obtengo la respuesta Test filtered que es lo que estás tratando de lograr.

Ejecuté el código siguiente en Apache Tomcat 7 y está funcionando sin excepciones.

Servlet:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("Test"); }

Filtrar:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("BEFORE filter"); PrintWriter out = response.getWriter(); CharResponseWrapper responseWrapper = new CharResponseWrapper( (HttpServletResponse) response); chain.doFilter(request, responseWrapper); String servletResponse = new String(responseWrapper.toString()); out.write(servletResponse + " filtered"); // Here you can change the response System.out.println("AFTER filter, original response: " + servletResponse); }

CharResponseWrapper (exactamente como el artículo)

public class CharResponseWrapper extends HttpServletResponseWrapper { private CharArrayWriter output; public String toString() { return output.toString(); } public CharResponseWrapper(HttpServletResponse response) { super(response); output = new CharArrayWriter(); } public PrintWriter getWriter() { return new PrintWriter(output); } }

web.xml

<servlet> <servlet-name>TestServlet</servlet-name> <servlet-class>TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestServlet</servlet-name> <url-pattern>/TestServlet</url-pattern> </servlet-mapping> <filter> <filter-name>TestFilter</filter-name> <filter-class>MyFilter</filter-class> </filter> <filter-mapping> <filter-name>TestFilter</filter-name> <url-pattern>/TestServlet/*</url-pattern> </filter-mapping>