自定义实现敏感词过滤
一、适用于低版本 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
@AllArgsConstructor
public class GetBodyGlobalFilter implements GlobalFilter, Ordered {
@Override
public 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);
}
}
@Override
public int getOrder() {
return -102;
}
}
2. 敏感词检测过滤器 SensitiveWordsFilter
/**
* 敏感词检测
*
*/
@Slf4j
@Component
@AllArgsConstructor
public 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;
}
}
贡献者
flycodeu
版权所有
版权归属:flycodeu