Web 應用需在響應輸出到瀏覽器前,統一攔截並替換敏感詞(如廣告詞、違禁詞、錯別字修正等)。核心思路:自定義 Response 包裝類緩存輸出內容 → Filter 中替換 → 再寫入原始 Response。
HttpCharacterResponseWrapperpublic class HttpCharacterResponseWrapper extends HttpServletResponseWrapper {
private final CharArrayWriter buffer = new CharArrayWriter();
public HttpCharacterResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public PrintWriter getWriter() {
return new PrintWriter(buffer); // 將輸出導向內存緩存
}
public CharArrayWriter getBuffer() {
return buffer;
}
}
關鍵點:覆寫 getWriter() 返回指向 CharArrayWriter 的 PrintWriter,實現「攔截輸出、內存緩存」。
OutputReplaceFilter@WebFilter(filterName = "OutputReplaceFilter", urlPatterns = "*.jsp")
public class OutputReplaceFilter implements Filter {
private Properties sensitiveWords = new Properties();
@Override
public void init(FilterConfig config) throws ServletException {
String file = config.getInitParameter("file"); // e.g. /WEB-INF/sensitive.properties
String realPath = config.getServletContext().getRealPath(file);
try (FileInputStream fis = new FileInputStream(realPath)) {
sensitiveWords.load(fis);
} catch (IOException e) {
throw new ServletException("加載敏感詞配置失敗", e);
}
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpCharacterResponseWrapper wrapper = new HttpCharacterResponseWrapper((HttpServletResponse) resp);
chain.doFilter(req, wrapper); // 讓後續鏈路寫入 wrapper
String content = wrapper.getBuffer().toString(); // 取出完整響應文本
// 遍歷替換
for (Object keyObj : sensitiveWords.keySet()) {
String key = (String) keyObj;
content = content.replace(key, sensitiveWords.getProperty(key));
}
// 寫回原始響應
PrintWriter out = resp.getWriter();
out.write(content);
out.println("<!-- Generated at " + new Date() + " -->");
}
@Override
public void destroy() { /* no-op */ }
}
/WEB-INF/sensitive.properties# 修正類
Chna = China
www.baidu.com.cn = www.baidu.com
# 替換類
色情 = **
賭博 = **
情色 = **
<filter>
<filter-name>OutputReplaceFilter</filter-name>
<filter-class>com.yzj.filter.OutputReplaceFilter</filter-class>
<init-param>
<param-name>file</param-name>
<param-value>/WEB-INF/sensitive.properties</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>OutputReplaceFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
JSP 頁面輸出:
Chna
色情
賭博
情色
www.baidu.com.cn
瀏覽器實際接收:
China
**
**
**
www.baidu.com
| 維度 | 建議 |
|---|---|
| 性能 | 大響應體建議用 StringBuilder 或流式替換(如 Aho-Corasick 多模匹配),避免多次 String.replace 產生大量臨時對象。 |
| 編碼 | 確保 request/response.setCharacterEncoding(\"UTF-8\"),Properties 文件存 UTF-8,防止中文亂碼。 |
| 範圍 | 僅攔截文本類響應(HTML/JSON/XML),二進制流(圖片/下載)應排除,可在 Filter 中判斷 Content-Type。 |
| Spring Boot | 改用 @Component + FilterRegistrationBean 註冊,屬性注入用 @Value 讀取 YAML/Properties。 |
| XSS 防護 | 本方案可順帶做 HTML 轉義,但專業場景建議引入 OWASP Java HTML Sanitizer。 |
CharArrayWriter 內存緩存。getWriter() 寫出最終內容給瀏覽器。此模式屬於裝飾器模式在 Servlet 規範中的經典應用,同樣可用於響應壓縮、加密、簽名、日誌記錄等橫切關注點處理。