refactor: 删除heritage-common异常和过滤器

- 移除所有异常处理类
- 移除XSS过滤器相关组件
- 移除拦截器组件
- 清理安全相关基础设施
This commit is contained in:
Leo 2025-10-17 11:08:11 +08:00
parent 7b7b767a5a
commit 45517ee01f
11 changed files with 0 additions and 768 deletions

View File

@ -1,30 +0,0 @@
package org.leocoder.heritage.common.exception;
import lombok.Getter;
import org.leocoder.heritage.common.enmus.common.IResultEnum;
/**
* @author : Leocoder
* @version 1.0
* @date 2025-06-28 14:09
* @description [业务异常]
*/
@Getter
public class BusinessException extends RuntimeException {
private Integer status = 500;
private String message = "业务异常,请联系管理员";
public BusinessException(int status, String message) {
super(message);
this.status = status;
this.message = message;
}
public BusinessException(IResultEnum resultEnum) {
super(resultEnum.message());
this.status = resultEnum.status();
this.message = resultEnum.message();
}
}

View File

@ -1,30 +0,0 @@
package org.leocoder.heritage.common.exception;
import lombok.Getter;
import org.leocoder.heritage.common.enmus.common.IResultEnum;
/**
* @author : Leocoder
* @version 1.0
* @date 2025-06-28 14:09
* @description [RateLimitException]
*/
@Getter
public class RateLimiterException extends RuntimeException {
private int status;
private String message;
public RateLimiterException(int status, String message) {
super(message);
this.status = status;
this.message = message;
}
public RateLimiterException(IResultEnum resultEnum) {
super(resultEnum.message());
this.status = resultEnum.status();
this.message = resultEnum.message();
}
}

View File

@ -1,29 +0,0 @@
package org.leocoder.heritage.common.exception;
import lombok.Getter;
import org.leocoder.heritage.common.enmus.common.IResultEnum;
/**
* @author : Leocoder
* @version 1.0
* @date 2025-06-28 14:09
* @description [RepeatSubmitException]
*/
@Getter
public class RepeatSubmitException extends RuntimeException {
private int status;
private String message;
public RepeatSubmitException(int status, String message) {
super(message);
this.status = status;
this.message = message;
}
public RepeatSubmitException(IResultEnum resultEnum) {
super(resultEnum.message());
this.status = resultEnum.status();
this.message = resultEnum.message();
}
}

View File

@ -1,45 +0,0 @@
package org.leocoder.heritage.common.exception.coder;
import lombok.Getter;
import org.leocoder.heritage.common.enmus.common.IResultEnum;
/**
* @author : Leocoder
* @version 1.0
* @date 2025-06-28 14:09
* @description [新增定义参数异常}
* RuntimeException 运行时异常只要报错即不会向下运行
*/
@Getter
public class ParamsException extends RuntimeException {
private Integer status = 500;
private String message = "自定义异常,请联系管理员";
public ParamsException() {
super("自定义异常,请联系管理员");
}
public ParamsException(String message) {
super(message);
this.message = message;
}
public ParamsException(Integer status) {
super("自定义异常,请联系管理员");
this.status = status;
}
public ParamsException(Integer status, String message) {
super(message);
this.status = status;
this.message = message;
}
// 枚举类不用可以删除
public ParamsException(IResultEnum resultEnum) {
super(resultEnum.message());
this.status = resultEnum.status();
this.message = resultEnum.message();
}
}

View File

@ -1,98 +0,0 @@
package org.leocoder.heritage.common.exception.coder;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.leocoder.heritage.common.enmus.common.IResultEnum;
/**
* @author : Leocoder
* @version 1.0
* @date 2025-06-28 14:09
* 用来代替if()else{}的判断
* flag == true的时候抛出参数异常
*/
public class YUtil {
/* 注意:使用 YUtil 工具类StringUtils.isBlank()、StringUtils.isNotBlank()flag为true则抛异常 */
/**
* @description [普通直接抛出异常]
* @author : Leocoder
*/
public static void isTrue(String message) {
throw new ParamsException(message);
}
/**
* @description [枚举直接抛出异常]
* @author : Leocoder
*/
public static void isTrue(IResultEnum resultEnum) {
throw new ParamsException(resultEnum);
}
/**
* @param flag 条件为true抛出异常
* @param message 异常信息
* @description [普通抛出异常]
* @author : Leocoder
*/
public static void isTrue(Boolean flag, String message) {
if (flag) {
throw new ParamsException(message);
}
}
/**
* @param flag 条件为true抛出异常
* @param resultEnum 异常信息
* @description [枚举抛出异常]
* @author : Leocoder
*/
public static void isTrue(Boolean flag, IResultEnum resultEnum) {
if (flag) {
throw new ParamsException(resultEnum);
}
}
/**
* @description [实体类抛出异常]
* @author : Leocoder
*/
public static void isNull(Object data, String message) {
if (ObjectUtils.isEmpty(data)) {
throw new ParamsException(message);
}
}
/**
* @description [实体类枚举抛出异常]
* @author : Leocoder
*/
public static void isNull(Object data, IResultEnum resultEnum) {
if (ObjectUtils.isEmpty(data)) {
throw new ParamsException(resultEnum);
}
}
/**
* @description [String枚举抛出异常]
* @author : Leocoder
*/
public static void isNull(String data, String message) {
if (StringUtils.isBlank(data)) {
throw new ParamsException(message);
}
}
/**
* @description [String枚举抛出异常]
* @author : Leocoder
*/
public static void isNull(String data, IResultEnum resultEnum) {
if (StringUtils.isBlank(data)) {
throw new ParamsException(resultEnum);
}
}
}

View File

@ -1,65 +0,0 @@
package org.leocoder.heritage.common.filter;
import cn.hutool.core.io.IoUtil;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author : Leocoder
* @description [构建可重复读取inputStream的request]
*/
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {
super(request);
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
body = IoUtil.readBytes(request.getInputStream(), false);
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public int available() throws IOException {
return body.length;
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}

View File

@ -1,65 +0,0 @@
package org.leocoder.heritage.common.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
/**
* @author : Leocoder
* @description [XssFilter]
*/
// xss跨站脚本攻击通过前端input将js脚本注入到后台
// sql注入将恶意的sql命令注入到后台数据库引擎执行
// csrf跨站请求伪造以用户身份在攻击页面对目标网站发起伪造用户操作的请求
// 其中xss和sql注入可以通过拦截请求并进行特殊字符过滤来防御而csrf需要进行referer检测和token校验进行防御
// 如果只做了referer检测在实际的第三方机构检测中csrf漏洞还是存在的所以需要进行token校验token校验在这里不做过多说明只进行referer检测
@Slf4j
@WebFilter
public class XssFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
// 升级SpringBoot 3.4.1后影响验证码无法正常显示
// response.setContentType("text/html;charset=utf-8");
/**
* 跨域设置
* 跨域由于浏览器的安全性限制不允许AJAX访问 协议不同域名不同端口号不同的 数据接口;
* 前后端都需要设置允许跨域;
*/
// if (response instanceof HttpServletResponse httpServletResponse) {
// // 通过在响应 header 中设置 * 来允许来自所有域的跨域请求访问
// httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
// // 通过对 Credentials 参数的设置就可以保持跨域 Ajax 时的 Cookie
// // 设置了Allow-CredentialsAllow-Origin就不能为*,需要指明具体的url域
// // httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
// // 请求方式
// httpServletResponse.setHeader("Access-Control-Allow-Methods", "*");
// // [预检请求]的返回结果[ Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息]可以被缓存多久
// httpServletResponse.setHeader("Access-Control-Max-Age", "86400");
// // 首部字段用于预检请求的响应其指明了实际请求中允许携带的首部字段
// httpServletResponse.setHeader("Access-Control-Allow-Headers", "*");
// }
// sql,xss过滤
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// log.info("XssFilter-original url{}ParameterMap{}", httpServletRequest.getRequestURI(), JSONObject.toJSONString(httpServletRequest.getParameterMap()));
XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper(httpServletRequest);
chain.doFilter(xssHttpServletRequestWrapper, response);
// log.info("XssFilter-doFilter url{}ParameterMap{}", xssHttpServletRequestWrapper.getRequestURI(), JSONObject.toJSONString(xssHttpServletRequestWrapper.getParameterMap()));
}
@Override
public void destroy() {
}
}

View File

@ -1,121 +0,0 @@
package org.leocoder.heritage.common.filter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
/**
* @author : Leocoder
* @description [XssHttpServletRequestWrapper]
*/
@Slf4j
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
private static final Set<String> notAllowedKeyWords = new HashSet<String>(0);
static {
String key = "and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+";
String[] keyStr = key.split("\\|");
notAllowedKeyWords.addAll(Arrays.asList(keyStr));
}
private final String currentUrl;
public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
currentUrl = servletRequest.getRequestURI();
}
/**
* 覆盖getParameter方法将参数名和参数值都做xss过滤
* 如果需要获得原始的值则通过super.getParameterValues(name)来获取
* getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
*/
@Override
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
if (value == null) {
return null;
}
return cleanXSS(value);
}
@Override
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = cleanXSS(values[i]);
}
return encodedValues;
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> values = super.getParameterMap();
if (values == null) {
return null;
}
Map<String, String[]> result = new HashMap<>();
for (String key : values.keySet()) {
String encodedKey = cleanXSS(key);
int count = values.get(key).length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = cleanXSS(values.get(key)[i]);
}
result.put(encodedKey, encodedValues);
}
return result;
}
/**
* 覆盖getHeader方法将参数名和参数值都做xss过滤
* 如果需要获得原始的值则通过super.getHeaders(name)来获取
* getHeaderNames 也可能需要覆盖
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (value == null) {
return null;
}
return cleanXSS(value);
}
private String cleanXSS(String valueP) {
// You'll need to remove the spaces from the html entities below
String value = valueP.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
value = value.replaceAll("'", "& #39;");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("script", "");
value = cleanSqlKeyWords(value);
return value;
}
private String cleanSqlKeyWords(String value) {
String paramValue = value;
for (String keyword : notAllowedKeyWords) {
if (paramValue.length() > keyword.length() + 4
&& (paramValue.contains(" " + keyword) || paramValue.contains(keyword + " ") || paramValue.contains(" " + keyword + " "))) {
String replacedString = "INVALID";
paramValue = StringUtils.replace(paramValue, keyword, replacedString);
log.warn(this.currentUrl + "已被过滤因为参数中包含不允许sql的关键词[" + keyword
+ "]" + "-[参数]" + value + "-[过滤后的参数]" + paramValue);
}
}
return paramValue;
}
}

View File

@ -1,162 +0,0 @@
package org.leocoder.heritage.common.interceptor;
import cn.hutool.core.collection.CollectionUtil;
import jakarta.annotation.Nullable;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.leocoder.heritage.common.constants.CoderCacheConstants;
import org.leocoder.heritage.common.constants.CoderConstants;
import org.leocoder.heritage.common.exception.coder.ParamsException;
import org.leocoder.heritage.common.utils.cache.RedisLimitUtil;
import org.leocoder.heritage.common.utils.cache.RedisUtil;
import org.leocoder.heritage.common.utils.ip.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author : Leocoder
* @description [黑白名单IP拦截]
*/
@Slf4j
@Component
public class CoderIpBlockListInterceptor implements HandlerInterceptor {
@Value("${Coder.globalLimit.enabled:false}")
private Boolean globalLimitEnabled;
@Value("${Coder.globalLimit.time:1}")
private Integer globalLimitTime;
@Value("${Coder.globalLimit.count:12}")
private Integer globalLimitCount;
@Value("${Coder.globalLimit.ban:false}")
private Boolean globalLimitBan;
@Autowired
private RedisUtil redisUtil;
@Autowired
private RedisLimitUtil redisLimitUtil;
@Override
public boolean preHandle(@Nullable HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Object handler) {
if (request != null) {
ipMatch(IpUtil.getIpAddr(request));
}
return true;
}
/**
* @description [IP匹配]
* @author : Leocoder
*/
private void ipMatch(String currentIp) {
// 校验IP有效性
if (StringUtils.isBlank(currentIp)) {
throw new ParamsException("IP地址获取失败禁止该用户访问");
}
// 提取真实IP[处理多IP情况]
currentIp = Arrays.stream(currentIp.split(","))
.findFirst()
.map(String::trim)
.orElseThrow(() -> new IllegalArgumentException("无效IP禁止访问"));
// 白名单Redis缓存数据
List<String> whiteIpKeyList = redisUtil.getKeysByPrefix(CoderCacheConstants.WHITELIST_IP_KEY);
boolean isWhite = false;
if (CollectionUtil.isNotEmpty(whiteIpKeyList)) {
for (String whiteIpKey : whiteIpKeyList) {
String whiteIp = redisUtil.getKey(whiteIpKey);
if (StringUtils.isNotEmpty(whiteIp)) {
String regex = whiteIp.replace("?", ".").replace("*", ".*");
Pattern compiledPattern = Pattern.compile(regex);
Matcher matcher = compiledPattern.matcher(currentIp);
if (matcher.matches()) {
isWhite = true;
// log.info("白名单IP[{}],请求已通过白名单检查", currentIp);
}
}
}
// 没有白名单Redis数据不进行校验
} else {
isWhite = CoderConstants.TRUE;
}
if (!isWhite) {
log.error("非白名单IP[{}],请求已拒绝", currentIp);
throw new ParamsException("暂时无法为您提供服务[WHITE]");
}
// 黑名单Redis缓存数据
boolean blackHasKey = redisUtil.hasKey(CoderCacheConstants.BLACKLIST_IP_KEY + currentIp);
if (blackHasKey) {
String blackIp = redisUtil.getKey(CoderCacheConstants.BLACKLIST_IP_KEY + currentIp);
if (StringUtils.isNotEmpty(blackIp)) {
String regex = blackIp.replace("?", ".").replace("*", ".*");
Pattern compiledPattern = Pattern.compile(regex);
Matcher matcher = compiledPattern.matcher(currentIp);
if (matcher.matches()) {
log.error("黑名单IP[{}],请求已拒绝", currentIp);
throw new ParamsException("暂时无法为您提供服务[BLACK]");
}
}
}
// log.info("IP[{}],通过白名单和黑名单检查,请求已通过", currentIp);
// 全局限流yml根据业务进行配置
if (globalLimitEnabled) {
checkRateLimit(currentIp);
}
}
public void checkRateLimit(String currentIp) {
String blackKey = CoderCacheConstants.BLACKLIST_IP_KEY + currentIp;
String limitKey = CoderCacheConstants.RATE_LIMIT_KEY + currentIp;
// 全局黑名单模式访问频繁直接拉黑
if (globalLimitBan) {
boolean blackHasKey = redisUtil.hasKey(blackKey);
if (blackHasKey) {
// 判断是否超出限流次数
if (!redisLimitUtil.limit(limitKey, globalLimitCount, globalLimitTime)) {
log.info("全局黑名单IP[EXIST]{}", currentIp);
String blackIp = redisUtil.getKey(blackKey);
if (StringUtils.isNotEmpty(blackIp)) {
redisUtil.setCacheObject(blackKey, currentIp);
}
throw new ParamsException("暂时无法为您提供服务[GLOBAL]");
}
} else {
// 判断是否超出限流次数
if (!redisLimitUtil.limit(limitKey, globalLimitCount, globalLimitTime)) {
log.info("全局黑名单IP[NULL]{}", currentIp);
redisUtil.setCacheObject(blackKey, currentIp);
throw new ParamsException("暂时无法为您提供服务[GLOBAL]");
}
}
// 全局限流模式访问频繁进行限流
} else {
// 判断是否超出限流次数
if (!redisLimitUtil.limit(limitKey, globalLimitCount, globalLimitTime)) {
log.info("全局限流IP{}", currentIp);
throw new ParamsException("访问过于频繁,请稍候再试[GLOBAL]");
}
}
}
}

View File

@ -1,30 +0,0 @@
package org.leocoder.heritage.common.interceptor;
import cn.hutool.core.lang.UUID;
import jakarta.annotation.Nullable;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* @author : Leocoder
* @description [CoderTrackIdInterceptor]
*/
@Component
public class CoderTrackIdInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Object handler) {
String traceId = String.format("%s - %s", request.getRequestURI(), UUID.fastUUID().toString(true));
// 可以考虑让客户端传入链路ID但需保证一定的复杂度唯一性如果没使用默认UUID自动生成
if (StringUtils.isNotBlank(request.getHeader("TRACE_ID"))){
traceId = request.getHeader("TRACE_ID");
}
MDC.put("traceId", traceId);
return true;
}
}

View File

@ -1,93 +0,0 @@
package org.leocoder.heritage.common.interceptor;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.extra.spring.SpringUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.core.utils.StringUtil;
import org.apache.commons.lang3.time.StopWatch;
import org.leocoder.heritage.common.filter.RepeatedlyRequestWrapper;
import org.leocoder.heritage.common.utils.json.JsonUtil;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import java.io.BufferedReader;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* web的调用时间统计拦截器
* dev环境有效
*
* @author : Leocoder
*/
@Slf4j
public class CoderWebInvokeTimeInterceptor implements HandlerInterceptor {
private final String prodProfile = "prod";
private final static ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!prodProfile.equals(SpringUtil.getActiveProfile())) {
String url = request.getMethod() + " " + request.getRequestURI();
// 打印请求参数
if (isJsonRequest(request)) {
String jsonParam = "";
if (request instanceof RepeatedlyRequestWrapper) {
BufferedReader reader = request.getReader();
jsonParam = IoUtil.read(reader);
}
log.info("[Coder]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam);
} else {
Map<String, String[]> parameterMap = request.getParameterMap();
if (MapUtil.isNotEmpty(parameterMap)) {
String parameters = JsonUtil.toJsonString(parameterMap);
log.info("[Coder]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters);
} else {
log.info("[Coder]开始请求 => URL[{}],无参数", url);
}
}
StopWatch stopWatch = new StopWatch();
KEY_CACHE.set(stopWatch);
stopWatch.start();
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (!prodProfile.equals(SpringUtil.getActiveProfile())) {
StopWatch stopWatch = KEY_CACHE.get();
stopWatch.stop();
log.info("[Coder]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime(TimeUnit.MILLISECONDS));
KEY_CACHE.remove();
}
}
/**
* 判断本次请求的数据类型是否为json
*
* @param request request
* @return boolean
*/
private boolean isJsonRequest(HttpServletRequest request) {
String contentType = request.getContentType();
if (contentType != null) {
return StringUtil.startsWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE);
}
return false;
}
}