Java 后端敏感词过滤方案
一、适用于低版本 Spring Boot 项目的敏感词过滤器
1. 敏感词过滤器类 SensitiveWordsFilter
import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import java.io.IOException;import java.util.*;
@Component@Slf4j@WebFilter(urlPatterns = "/*", filterName = "SensitiveWordsFilter")public class SensitiveWordsFilter implements Filter {
// 敏感词列表 private final List<String> sensitiveWords = Arrays.asList("insert ", "update ", "delete ", "create ", "alter ", "drop ", "truncate ");
@Override public void init(FilterConfig filterConfig) { // 可选初始化逻辑 }
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request;
// 检查请求头 boolean hasSensitiveHeader = Collections.list(httpRequest.getHeaderNames()).stream() .anyMatch(header -> containsSensitiveWord(httpRequest.getHeader(header))); if (hasSensitiveHeader) { log.error("请求头中存在敏感词"); }
// 检查请求体(仅对 POST 请求处理) String method = httpRequest.getMethod(); if (StringPool.POST.equalsIgnoreCase(method)) { SensitiveWordsRequestWrapper requestWrapper = new SensitiveWordsRequestWrapper(httpRequest); String body = requestWrapper.getBody(); if (containsSensitiveWord(body)) { log.error("请求体中存在敏感词"); } }
// 检查请求参数 Map<String, String[]> parameterMap = httpRequest.getParameterMap(); boolean hasSensitiveParameter = parameterMap.values().stream() .flatMap(Arrays::stream) .anyMatch(this::containsSensitiveWord); if (hasSensitiveParameter) { log.error("请求参数中存在敏感词"); }
chain.doFilter(request, response); }
@Override public void destroy() { // 可选清理逻辑 }
private boolean containsSensitiveWord(String text) { if (StringUtil.isNotBlank(text)) { text = text.toLowerCase(); return sensitiveWords.stream().anyMatch(text::contains); } return false; }}2. 请求体包装类 SensitiveWordsRequestWrapper
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.nio.charset.StandardCharsets;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;
public class SensitiveWordsRequestWrapper extends HttpServletRequestWrapper {private final String body;
public SensitiveWordsRequestWrapper(HttpServletRequest request) throws IOException { super(request); StringBuilder sb = new StringBuilder(); try (BufferedReader reader = new BufferedReader( new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8))) { char[] buffer = new char[512]; int bytesRead; while ((bytesRead = reader.read(buffer)) > 0) { sb.append(buffer, 0, bytesRead); } } this.body = sb.toString(); }
public String getBody() { return this.body; }}3. 常量类 StringPool
public interface StringPool { String AMPERSAND = "&"; String AND = "and"; String AT = "@"; String ASTERISK = "*"; String STAR = "*"; String SLASH = "/"; String BACK_SLASH = "\\"; String DOUBLE_SLASH = "#//"; String COLON = ":"; String COMMA = ","; String DASH = "-"; String DOLLAR = "$"; String DOT = "."; String EMPTY = ""; String EMPTY_JSON = "{}"; String EQUALS = "="; String FALSE = "false"; String HASH = "#"; String HAT = "^"; String LEFT_BRACE = "{"; String LEFT_BRACKET = "("; String LEFT_CHEV = "<"; String NEWLINE = "\n"; String N = "n"; String NO = "no"; String NULL = "null"; String OFF = "off"; String ON = "on"; String PERCENT = "%"; String PIPE = "|"; String PLUS = "+"; String QUESTION_MARK = "?"; String EXCLAMATION_MARK = "!"; String QUOTE = "\""; String RETURN = "\r"; String TAB = "\t"; String RIGHT_BRACE = "}"; String RIGHT_BRACKET = ")"; String RIGHT_CHEV = ">"; String SEMICOLON = ";"; String SINGLE_QUOTE = "'"; String BACKTICK = "`"; String SPACE = " "; String TILDA = "~"; String LEFT_SQ_BRACKET = "["; String RIGHT_SQ_BRACKET = "]"; String TRUE = "true"; String UNDERSCORE = "_"; String UTF_8 = "UTF-8"; String GBK = "GBK"; String ISO_8859_1 = "ISO-8859-1"; String Y = "y"; String YES = "yes"; String ONE = "1"; String ZERO = "0"; String MINUS_ONE = "-1"; String DOLLAR_LEFT_BRACE = "${"; String UNKNOWN = "unknown"; String GET = "GET"; String POST = "POST";}二、适用于 Spring Cloud Gateway 的全局敏感词过滤方案
1. 请求体预读取过滤器 GetBodyGlobalFilter
@Component@AllArgsConstructorpublic class GetBodyGlobalFilter implements GlobalFilter, Ordered {
@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { if (HttpMethod.POST.equals(exchange.getRequest().getMethod()) && MediaType.APPLICATION_JSON.equalsTypeAndSubtype(exchange.getRequest().getHeaders().getContentType())) { return DataBufferUtils.join(exchange.getRequest().getBody()) .flatMap(dataBuffer -> { DataBufferUtils.retain(dataBuffer); Flux<DataBuffer> cachedFlux = Flux .defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount()))); ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator( exchange.getRequest()) { @Override public Flux<DataBuffer> getBody() { return cachedFlux; } }; return chain.filter(exchange.mutate().request(mutatedRequest).build()); }); } else { return chain.filter(exchange); }}
@Overridepublic int getOrder() { return -102;}}2. 敏感词检测过滤器 SensitiveWordsFilter
/** * 敏感词检测 * */@Slf4j@Component@AllArgsConstructorpublic class SensitiveWordsFilter implements GlobalFilter, Ordered {
private final ObjectMapper objectMapper;
// 敏感词列表 private final List<String> sensitiveWords = Arrays.asList("insert ", "update ", "delete ", "create ", "alter ", "drop ", "truncate ");
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse resp = exchange.getResponse();
// 检查请求头中是否包含敏感词 boolean hasSensitiveHeader = request.getHeaders().entrySet().stream() .anyMatch(entry -> entry.getValue().stream().anyMatch(this::containsSensitiveWord)); if (hasSensitiveHeader) { return unAuth(resp, "请求头中存在敏感词"); }
if (HttpMethod.POST.equals(exchange.getRequest().getMethod()) && MediaType.APPLICATION_JSON.equalsTypeAndSubtype(exchange.getRequest().getHeaders().getContentType())) { // 检查请求体中是否包含敏感词 Flux<DataBuffer> body = request.getBody(); AtomicReference<String> bodyRef = new AtomicReference<>(); body.subscribe(buffer -> { CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); DataBufferUtils.release(buffer); bodyRef.set(charBuffer.toString()); }); String rbody = bodyRef.get(); if (containsSensitiveWord(rbody)) { return unAuth(resp, "请求体中存在敏感词"); } }
// 检查请求参数中是否包含敏感词 String queryParams = request.getURI().getQuery(); if (containsSensitiveWord(queryParams)) { return unAuth(resp, "请求参数中存在敏感词"); }
// 如果没有敏感词,继续处理请求 return chain.filter(exchange); }
private Mono<Void> unAuth(ServerHttpResponse resp, String msg) { resp.setStatusCode(HttpStatus.UNAUTHORIZED); resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); String result = ""; try { result = objectMapper.writeValueAsString(ResponseProvider.unAuth(msg)); } catch (JsonProcessingException e) { log.error(e.getMessage(), e); } DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8)); return resp.writeWith(Flux.just(buffer)); }
private boolean containsSensitiveWord(String text) { if (StringUtils.isNotBlank(text)) { text = text.toLowerCase(); for (String sensitiveWord : sensitiveWords) { if (text.contains(sensitiveWord)) { return true; } } } return false; }
@Override public int getOrder() { return -101; }}
评论