首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

無源碼老舊項目 IP 變更方案:基於 Servlet Filter 實現響應內容動態替換

     編輯:Java 相關知識

歷史項目無源碼、IP 寫死在編譯後類中,反編譯修改不可行。靈機一動:IP 最終以字符串形式輸出到頁面,**攔截響應輸出流,字符串替換後再寫入客戶端**。本文提供基於 HttpServletResponseWrapper 包裝輸出流 + Filter 替換內容的完整可用方案。

一、核心思路

  1. 自定義 ResponseWrapper extends HttpServletResponseWrapper,內部用 ByteArrayOutputStream 緩衝所有輸出數據。
  2. 重寫 getOutputStream()、getWriter() 返回包裝流,攔截 ServletOutputStream.write() 和 PrintWriter.write()。
  3. Filter 鏈執行完畢後,從 Wrapper 取出完整響應字節數組,轉字符串,按配置映射替換 (如 IP、域名)。
  4. 將替換後內容寫入真實 HttpServletResponse 返回客戶端。

二、ResponseWrapper 實現 (關鍵:包裝 ServletOutputStream)

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 final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    private ServletOutputStream servletOutputStream;
    private PrintWriter printWriter;

    public ResponseWrapper(HttpServletResponse response) throws IOException {
        super(response);
        // 包裝 ServletOutputStream,寫入時導向 buffer
        servletOutputStream = new ServletOutputStream() {
            @Override public void write(int b) throws IOException { buffer.write(b); }
            @Override public void write(byte[] b, int off, int len) throws IOException { buffer.write(b, off, len); }
            @Override public void write(byte[] b) throws IOException { buffer.write(b); }
            @Override public void flush() throws IOException { buffer.flush(); }
            @Override public void close() throws IOException { buffer.close(); }
        };
        // 包裝 PrintWriter,同樣導向 buffer
        printWriter = new PrintWriter(new OutputStreamWriter(buffer, getCharacterEncoding()));
    }

    @Override public ServletOutputStream getOutputStream() throws IOException { return servletOutputStream; }
    @Override public PrintWriter getWriter() throws UnsupportedEncodingException { return printWriter; }
    @Override public void flushBuffer() throws IOException {
        if (servletOutputStream != null) servletOutputStream.flush();
        if (printWriter != null) printWriter.flush();
    }
    @Override public void reset() { buffer.reset(); super.reset(); }

    // 供 Filter 獲取完整響應數據
    public byte[] getResponseData() throws IOException {
        flushBuffer(); // 強制刷新兩個流
        return buffer.toByteArray();
    }
}

三、IPChangeFilter 實現

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.*;
import javax.servlet.http.*;

public class IPChangeFilter implements Filter {
    private static final String BUNDLE_NAME = "org.mice.ipchange.ipchange";
    private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 防亂碼:強制指定編碼 (根據項目實際調整)
        response.setContentType("text/html;charset=GBK");
        
        HttpServletResponse httpResp = (HttpServletResponse) response;
        ResponseWrapper wrapper = new ResponseWrapper(httpResp);
        
        // 1. 放行鏈路,業務邏輯寫入 wrapper 緩衝區
        chain.doFilter(request, wrapper);
        
        // 2. 獲取完整響應內容
        String content = new String(wrapper.getResponseData(), "GBK"); // 指定解碼編碼
        
        // 3. 替換邏輯
        String replaced = replaceText(content);
        
        // 4. 寫入真實響應
        httpResp.setContentType("text/html;charset=GBK");
        try (PrintWriter out = httpResp.getWriter()) {
            out.write(replaced);
        }
    }

    private String replaceText(String content) {
        Enumeration<String> keys = RESOURCE_BUNDLE.getKeys();
        while (keys.hasMoreElements()) {
            String oldVal = keys.nextElement();
            String newVal = RESOURCE_BUNDLE.getString(oldVal);
            content = content.replaceAll(oldVal, newVal); // 正則替換,若需精確匹配用 replace
        }
        return content;
    }

    public static String getString(String key) {
        try { return RESOURCE_BUNDLE.getString(key); }
        catch (MissingResourceException e) { return key; }
    }

    @Override public void init(FilterConfig config) throws ServletException {}
    @Override public void destroy() {}
}

四、配置文件 ipchange.properties

# 格式: 被替換的舊值 = 新值
# 支持正則,特殊字符需轉義
192\.168\.1\.100 = 203.0.113.50
old\.domain\.com = new.domain.com
localhost:8080 = prod.server.com

放置在 classpath 根目錄 (如 src/main/resources/org/mice/ipchange/ipchange.properties)。

五、web.xml 註冊 Filter

<filter>
    <filter-name>ipChangeFilter</filter-name>
    <filter-class>org.mice.ipchange.IPChangeFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>ipChangeFilter</filter-name>
    <url-pattern>/*</url-pattern> <!-- 攔截所有請求 -->
</filter-mapping>

六、關鍵注意事項與優化

場景問題對策
二進制響應 (圖片/文件下載)字符串替換破壞二進制內容在 Filter 中判斷 Content-Type 非文本類型直接放行不處理
GZIP 壓縮響應獲取到的是壓縮字節,無法替換禁用壓縮,或在壓縮前攔截 (需更深層 Filter 順序)
大響應體 (超大 HTML)ByteArrayOutputStream 內存暴漲設置閾值超限時降級不處理,或使用流式替換 (複雜)
編碼不一致解碼亂碼、替換失效強制統一 GBK/UTF-8,從 response.getCharacterEncoding() 獲取

七、總結

利用 Servlet Filter + ResponseWrapper 緩衝全量響應 + 字符串替換,在無源碼、無法修改業務邏輯的約束下,低成本實現 IP/域名等靜態字符串的動態替換。核心在於 包裝輸出流攔截寫入 與 配置驅動替換規則 的解耦設計。適用於遷移上雲、域名變更、內外網映射等場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved