Browse Source

feat(async): 添加异步任务配置和线程池管理

- 新增异步任务配置类 AsyncConfig,支持多线程池管理
- 配置核心业务线程池和 IO 密集型任务线程池
- 实现自定义线程工厂,支持线程优先级和异常处理
- 提供 DirectAsyncService 和 DirectCompletableFutureService 异步服务示例
- 支持通过 ThreadPoolProperties 配置线程池参数
- 添加异步任务异常处理机制和优雅关闭配置
wzq 2 weeks ago
parent
commit
b943be085c

+ 27 - 0
src/main/java/com/zsElectric/boot/common/xss/Xss.java

@@ -0,0 +1,27 @@
+package com.zsElectric.boot.common.xss;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 自定义xss校验注解
+ *
+ * @author Lion Li
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
+@Constraint(validatedBy = {XssValidator.class})
+public @interface Xss {
+
+    String message() default "不允许任何脚本运行";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+
+}

+ 58 - 0
src/main/java/com/zsElectric/boot/common/xss/XssFilter.java

@@ -0,0 +1,58 @@
+package com.zsElectric.boot.common.xss;
+
+import com.youlai.boot.common.util.SpringUtils;
+import com.youlai.boot.common.util.StringUtils;
+import jakarta.servlet.*;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.http.HttpMethod;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 防止XSS攻击的过滤器
+ *
+ * @author ruoyi
+ */
+public class XssFilter implements Filter {
+    /**
+     * 排除链接
+     */
+    public List<String> excludes = new ArrayList<>();
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        XssProperties properties = SpringUtils.getBean(XssProperties.class);
+        excludes.addAll(properties.getExcludeUrls());
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+        throws IOException, ServletException {
+        HttpServletRequest req = (HttpServletRequest) request;
+        HttpServletResponse resp = (HttpServletResponse) response;
+        if (handleExcludeURL(req, resp)) {
+            chain.doFilter(request, response);
+            return;
+        }
+        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
+        chain.doFilter(xssRequest, response);
+    }
+
+    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {
+        String url = request.getServletPath();
+        String method = request.getMethod();
+        // GET DELETE 不过滤
+        if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) {
+            return true;
+        }
+        return StringUtils.matches(url, excludes);
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}

+ 134 - 0
src/main/java/com/zsElectric/boot/common/xss/XssHttpServletRequestWrapper.java

@@ -0,0 +1,134 @@
+package com.zsElectric.boot.common.xss;
+
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HtmlUtil;
+import com.youlai.boot.common.util.StringUtils;
+import jakarta.servlet.ReadListener;
+import jakarta.servlet.ServletInputStream;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * XSS过滤处理
+ *
+ * @author ruoyi
+ */
+public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
+    /**
+     * @param request
+     */
+    public XssHttpServletRequestWrapper(HttpServletRequest request) {
+        super(request);
+    }
+
+    @Override
+    public String getParameter(String name) {
+        String value = super.getParameter(name);
+        if (value == null) {
+            return null;
+        }
+        return HtmlUtil.cleanHtmlTag(value).trim();
+    }
+
+    @Override
+    public Map<String, String[]> getParameterMap() {
+        Map<String, String[]> valueMap = super.getParameterMap();
+        if (MapUtil.isEmpty(valueMap)) {
+            return valueMap;
+        }
+        // 避免某些容器不允许改参数的情况 copy一份重新改
+        Map<String, String[]> map = new HashMap<>(valueMap.size());
+        map.putAll(valueMap);
+        for (Map.Entry<String, String[]> entry : map.entrySet()) {
+            String[] values = entry.getValue();
+            if (values != null) {
+                int length = values.length;
+                String[] escapseValues = new String[length];
+                for (int i = 0; i < length; i++) {
+                    // 防xss攻击和过滤前后空格
+                    escapseValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
+                }
+                map.put(entry.getKey(), escapseValues);
+            }
+        }
+        return map;
+    }
+
+    @Override
+    public String[] getParameterValues(String name) {
+        String[] values = super.getParameterValues(name);
+        if (ArrayUtil.isEmpty(values)) {
+            return values;
+        }
+        int length = values.length;
+        String[] escapseValues = new String[length];
+        for (int i = 0; i < length; i++) {
+            // 防xss攻击和过滤前后空格
+            escapseValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
+        }
+        return escapseValues;
+    }
+
+    @Override
+    public ServletInputStream getInputStream() throws IOException {
+        // 非json类型,直接返回
+        if (!isJsonRequest()) {
+            return super.getInputStream();
+        }
+
+        // 为空,直接返回
+        String json = StrUtil.str(IoUtil.readBytes(super.getInputStream(), false), StandardCharsets.UTF_8);
+        if (StringUtils.isEmpty(json)) {
+            return super.getInputStream();
+        }
+
+        // xss过滤
+        json = HtmlUtil.cleanHtmlTag(json).trim();
+        byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
+        final ByteArrayInputStream bis = IoUtil.toStream(jsonBytes);
+        return new ServletInputStream() {
+            @Override
+            public boolean isFinished() {
+                return true;
+            }
+
+            @Override
+            public boolean isReady() {
+                return true;
+            }
+
+            @Override
+            public int available() throws IOException {
+                return jsonBytes.length;
+            }
+
+            @Override
+            public void setReadListener(ReadListener readListener) {
+            }
+
+            @Override
+            public int read() throws IOException {
+                return bis.read();
+            }
+        };
+    }
+
+    /**
+     * 是否是Json请求
+     */
+    public boolean isJsonRequest() {
+        String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
+        return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
+    }
+}

+ 28 - 0
src/main/java/com/zsElectric/boot/common/xss/XssProperties.java

@@ -0,0 +1,28 @@
+package com.zsElectric.boot.common.xss;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * xss过滤 配置属性
+ *
+ * @author Lion Li
+ */
+@Data
+@ConfigurationProperties(prefix = "xss")
+public class XssProperties {
+
+    /**
+     * Xss开关
+     */
+    private Boolean enabled;
+
+    /**
+     * 排除路径
+     */
+    private List<String> excludeUrls = new ArrayList<>();
+
+}

+ 20 - 0
src/main/java/com/zsElectric/boot/common/xss/XssValidator.java

@@ -0,0 +1,20 @@
+package com.zsElectric.boot.common.xss;
+
+import cn.hutool.core.util.ReUtil;
+import cn.hutool.http.HtmlUtil;
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+/**
+ * 自定义xss校验注解实现
+ *
+ * @author Lion Li
+ */
+public class XssValidator implements ConstraintValidator<Xss, String> {
+
+    @Override
+    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
+        return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value);
+    }
+
+}