ServletResponse 包装器 ResponseWrapper

本文介绍了一种解决历史项目中固定IP问题的方法,通过使用Filter在页面中处理与IP相关的内容,避免直接修改源代码。具体实现包括创建一个自定义的响应包装类来存储和操作数据,并在Filter中对数据进行处理和替换。

一个历史项目,客户要求改ip,由于系统较为久远,没有源代码,通过反编译后发现ip写死在类中,
反编译后的代码基本没法进行修改,分析了代码后,发现与ip相关的内容为返回到页面中的字符串,
所以,想到了使用Filter,在Filter中处理这些字符串,觉得可行,便查询了一些资料,试验成功。
以下这个类是从网上找到的,试验成功后发现页面已经关了,对原作者表示抱歉的同时表示感谢。

package org.mice.ipchange;  
       
import java.io.ByteArrayOutputStream;  
import java.io.IOException;  
import java.io.OutputStreamWriter;  
import java.io.PrintWriter;  
import java.io.UnsupportedEncodingException;  
       
import javax.servlet.ServletOutputStream;  
import javax.servlet.http.HttpServletResponse;  
import javax.servlet.http.HttpServletResponseWrapper;  
       
public class ResponseWrapper extends HttpServletResponseWrapper {  
    private ByteArrayOutputStream buffer = null;  
    private ServletOutputStream out = null;  
    private PrintWriter writer = null;  
       
    public ResponseWrapper(HttpServletResponse resp) throws IOException {  
        super(resp);  
        buffer = new ByteArrayOutputStream();// 真正存储数据的流  
        out = new WapperedOutputStream(buffer);  
        writer = new PrintWriter(new OutputStreamWriter(buffer,  
                this.getCharacterEncoding()));  
    }  
       
    // 重载父类获取outputstream的方法  
    public ServletOutputStream getOutputStream() throws IOException {  
        return out;  
    }  
       
    // 重载父类获取writer的方法  
    public PrintWriter getWriter() throws UnsupportedEncodingException {  
        return writer;  
    }  
       
    // 重载父类获取flushBuffer的方法  
    public void flushBuffer() throws IOException {  
        if (out != null) {  
            out.flush();  
        }  
        if (writer != null) {  
            writer.flush();  
        }  
    }  
       
    public void reset() {  
        buffer.reset();  
    }  
       
    public byte[] getResponseData() throws IOException {  
        // 将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据  
        flushBuffer();  
        return buffer.toByteArray();  
    }  
           
     //内部类,对ServletOutputStream进行包装    
    private class WapperedOutputStream extends ServletOutputStream{    
        private ByteArrayOutputStream bos=null;    
        public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException{    
            bos=stream;    
        }    
        public void write(int b) throws IOException{    
            bos.write(b);    
        }    
    }    
}


package org.mice.ipchange;  
       
import java.io.IOException;  
import java.io.PrintWriter;  
import java.util.Enumeration;  
import java.util.MissingResourceException;  
import java.util.ResourceBundle;  
       
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 javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
       
public class IPChangeFilter implements Filter {  
       
    public void doFilter(ServletRequest request, ServletResponse response,  
            FilterChain chain) throws IOException, ServletException {  
        // 需要设置这个,以防乱码  
        response.setContentType("text/html;charset=GBK");  
       
        HttpServletRequest httpReq = (HttpServletRequest) request;  
        HttpServletResponse httpResp = (HttpServletResponse) response;  
       
        ResponseWrapper respWrapper = new ResponseWrapper(httpResp);  
       
        chain.doFilter(request, respWrapper);  
        // weblogic8不支持response.setCharacterEncoding  
        // response.setCharacterEncoding("GBK");  
               
        String content = new String(respWrapper.getResponseData());  
        String result = replaceText(content);  
        httpResp.setContentType("text/html;charset=GBK");  
        PrintWriter out = httpResp.getWriter();  
        out.println(result);  
        out.close();  
    }  
       
    /**
     * 替换内容
     */
    private String replaceText(String content) {  
        Enumeration enumes = RESOURCE_BUNDLE.getKeys();  
        while (enumes.hasMoreElements()) {  
            String o = enumes.nextElement().toString();  
            content = content.replaceAll(o, RESOURCE_BUNDLE.getString(o));  
        }  
        return content;  
    }  
    // 替换文字的配置文件路径  
    private static final String BUNDLE_NAME = "org.mice.ipchange.ipchange";  
    // 加载配置文件  
    private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle  
            .getBundle(BUNDLE_NAME);  
           
    // 获取替换文字  
    public static String getString(String key) {  
        try {  
            return RESOURCE_BUNDLE.getString(key);  
        } catch (MissingResourceException e) {  
            e.printStackTrace();  
        }  
        return key;  
    }  
           
    public void init(FilterConfig arg0) throws ServletException {  
        // TODO Auto-generated method stub  
    }  
       
       
    public void destroy() {  
        // TODO Auto-generated method stub  
    }  
}



import org.owasp.esapi.ESAPI; import org.owasp.esapi.Encoder; import org.owasp.esapi.Validator; import org.owasp.esapi.errors.ValidationException; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import java.io.CharArrayWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Stack; public class UnifiedXSSFilter implements Filter { private Encoder encoder; private Validator validator; @Override public void init(FilterConfig filterConfig) { encoder = ESAPI.encoder(); validator = ESAPI.validator(); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 1. 创建安全的请求包装器 SafeRequestWrapper safeRequest = new SafeRequestWrapper((HttpServletRequest) request); // 2. 创建响应包装器(用于捕获输出) ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response); // 3. 执行过滤器链(使用安全请求和响应包装器) chain.doFilter(safeRequest, responseWrapper); // 4. 获取原始响应内容 String content = responseWrapper.toString(); // 5. 应用上下文感知编码 String safeContent = contextAwareEncode(content); // 6. 写回安全内容 response.getWriter().write(safeContent); } // ================ 请求处理部分 ================ private class SafeRequestWrapper extends HttpServletRequestWrapper { private final Map<String, String[]> sanitizedParams = new HashMap<>(); public SafeRequestWrapper(HttpServletRequest request) { super(request); sanitizeParameters(request); } private void sanitizeParameters(HttpServletRequest request) { Enumeration<String> paramNames = request.getParameterNames(); while (paramNames.hasMoreElements()) { String paramName = paramNames.nextElement(); String[] values = request.getParameterValues(paramName); String[] sanitizedValues = new String[values.length]; for (int i = 0; i < values.length; i++) { sanitizedValues[i] = sanitizeInput(values[i]); } sanitizedParams.put(paramName, sanitizedValues); } } @Override public String getParameter(String name) { String[] values = getParameterValues(name); return (values != null && values.length > 0) ? values[0] : null; } @Override public String[] getParameterValues(String name) { return sanitizedParams.get(name); } @Override public Map<String, String[]> getParameterMap() { return sanitizedParams; } // 输入清理方法 private String sanitizeInput(String input) { if (input == null) return null; try { // 1. 规范化输入 String canonical = encoder.canonicalize(input); // 2. 验证输入 if (validator.isValidInput("Parameter", canonical, "SafeString", 2000, false)) { return canonical; } // 3. 验证失败时进行安全编码 return encoder.encodeForHTML(canonical); } catch (ValidationException e) { ESAPI.getLogger(getClass()).warning( "Input validation failed: " + e.getMessage()); return encoder.encodeForHTML(input); } } } // ================ 响应处理部分 ================ // 上下文感知编码方法 private String contextAwareEncode(String html) { if (html == null || html.isEmpty()) return ""; StringBuilder result = new StringBuilder(html.length() + 100); Stack<String> tagStack = new Stack<>(); int index = 0; int length = html.length(); while (index < length) { char current = html.charAt(index); // 处理标签开始 if (current == '<') { int tagStart = index; int tagEnd = html.indexOf('>', tagStart); if (tagEnd == -1) { // 未闭合的标签,直接编码剩余内容 result.append(encoder.encodeForHTML(html.substring(tagStart))); break; } String tag = html.substring(tagStart + 1, tagEnd).trim(); String tagName = getTagName(tag); // 处理结束标签 if (tag.startsWith("/")) { if (!tagStack.isEmpty() && tagStack.peek().equals(tagName)) { tagStack.pop(); } result.append(html.substring(tagStart, tagEnd + 1)); index = tagEnd + 1; continue; } // 处理自闭合标签 boolean selfClosing = tag.endsWith("/"); if (selfClosing) { tag = tag.substring(0, tag.length() - 1).trim(); } // 提取标签属性 String tagContent = tag; int spaceIndex = tag.indexOf(' '); if (spaceIndex != -1) { tagName = tag.substring(0, spaceIndex); tagContent = tag.substring(spaceIndex + 1); } // 处理标签属性 String safeTag = encodeTagAttributes(tagName, tagContent); // 构建安全标签 result.append('<').append(safeTag); if (selfClosing) result.append('/'); result.append('>'); // 压入标签栈(非自闭合标签) if (!selfClosing && !"meta".equalsIgnoreCase(tagName) && !"link".equalsIgnoreCase(tagName) && !"img".equalsIgnoreCase(tagName)) { tagStack.push(tagName.toLowerCase()); } index = tagEnd + 1; continue; } // 处理标签内容(根据上下文编码) if (!tagStack.isEmpty()) { String currentTag = tagStack.peek(); int contentEnd = html.indexOf('<', index); if (contentEnd == -1) contentEnd = length; String contentPart = html.substring(index, contentEnd); // 根据当前标签类型进行不同编码 if ("script".equals(currentTag)) { result.append(encodeJavaScriptContent(contentPart)); } else if ("style".equals(currentTag)) { result.append(encodeCSSContent(contentPart)); } else { result.append(encoder.encodeForHTML(contentPart)); } index = contentEnd; } else { // 非标签内容直接编码 int nextTag = html.indexOf('<', index); if (nextTag == -1) nextTag = length; String text = html.substring(index, nextTag); result.append(encoder.encodeForHTML(text)); index = nextTag; } } return result.toString(); } // 获取标签名 private String getTagName(String tag) { int spaceIndex = tag.indexOf(' '); if (spaceIndex != -1) { return tag.substring(0, spaceIndex).toLowerCase(); } return tag.toLowerCase(); } // 编码标签属性 private String encodeTagAttributes(String tagName, String attributes) { if (attributes == null || attributes.isEmpty()) { return tagName; } StringBuilder result = new StringBuilder(tagName); int index = 0; int length = attributes.length(); while (index < length) { // 跳过空白字符 while (index < length && Character.isWhitespace(attributes.charAt(index))) { index++; } if (index >= length) break; // 获取属性名 int nameStart = index; while (index < length && !Character.isWhitespace(attributes.charAt(index)) && attributes.charAt(index) != '=') { index++; } String attrName = attributes.substring(nameStart, index); // 处理等号 if (index < length && attributes.charAt(index) == '=') { index++; // 跳过空白 while (index < length && Character.isWhitespace(attributes.charAt(index))) { index++; } // 获取属性值 char quote = '\0'; if (index < length && (attributes.charAt(index) == '"' || attributes.charAt(index) == '\'')) { quote = attributes.charAt(index); index++; } int valueStart = index; while (index < length) { if (quote != '\0' && attributes.charAt(index) == quote) { break; } if (quote == '\0' && Character.isWhitespace(attributes.charAt(index))) { break; } index++; } String attrValue = (valueStart < index) ? attributes.substring(valueStart, index) : ""; // 根据属性上下文编码 String encodedValue = encodeAttributeValue(tagName, attrName, attrValue); // 添加属性 result.append(' ').append(attrName).append("=\"") .append(encodedValue).append('\"'); if (quote != '\0' && index < length && attributes.charAt(index) == quote) { index++; // 跳过结束引号 } } else { // 没有值的属性 result.append(' ').append(attrName); } } return result.toString(); } // 根据属性上下文编码 private String encodeAttributeValue(String tagName, String attrName, String value) { // 处理特殊属性(href, src, style, event handlers) String lowerAttr = attrName.toLowerCase(); String lowerTag = tagName.toLowerCase(); if ("href".equals(lowerAttr) || "src".equals(lowerAttr)) { // URL属性 return encoder.encodeForHTMLAttribute(encoder.encodeForURL(value)); } else if ("style".equals(lowerAttr)) { // CSS属性 return encoder.encodeForCSS(value); } else if (lowerAttr.startsWith("on") && lowerAttr.length() > 2) { // 事件处理属性 return encoder.encodeForJavaScript(value); } else if ("value".equals(lowerAttr) && "input".equals(lowerTag)) { // 输入框的value属性 return encoder.encodeForHTMLAttribute(value); } else { // 普通属性 return encoder.encodeForHTMLAttribute(value); } } // JavaScript内容编码 private String encodeJavaScriptContent(String content) { // 保留合法JavaScript,仅编码危险部分 return encoder.encodeForJavaScript(content) .replace("\\u003C", "<") // 恢复合法比较操作符 .replace("\\u003E", ">"); } // CSS内容编码 private String encodeCSSContent(String content) { return encoder.encodeForCSS(content); } // ================ 响应包装器 ================ private static class ResponseWrapper extends HttpServletResponseWrapper { private final CharArrayWriter charWriter = new CharArrayWriter(); public ResponseWrapper(HttpServletResponse response) { super(response); } @Override public PrintWriter getWriter() { return new PrintWriter(charWriter); } @Override public String toString() { return charWriter.toString(); } } } 代码中可能存在outStrmes输出 String content = responseWrapper.toString(); 无法获原始响应 如何更改在我的代码里修改
最新发布
08-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值