diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index aa9e58d5..ea8ea841 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -78,16 +78,6 @@ - - - fail - - - false - - cug-jetty-patch - file:///${project.basedir}/tools/jetty-security-patch/repository - nutz https://jfrog.nutz.cn/artifactory/libs-release @@ -144,29 +134,36 @@ - org.eclipse.jetty - jetty-http - ${jetty.cug-patch.version} + javax.servlet + javax.servlet-api + 4.0.1 + provided - org.eclipse.jetty - jetty-server - ${jetty.cug-patch.version} + javax.websocket + javax.websocket-api + 1.1 + provided - org.eclipse.jetty - jetty-security - ${jetty.cug-patch.version} + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} - org.eclipse.jetty - jetty-client - ${jetty.cug-patch.version} + org.apache.tomcat.embed + tomcat-embed-websocket + ${tomcat.version} - org.eclipse.jetty - jetty-jaspi - ${jetty.cug-patch.version} + org.apache.tomcat.embed + tomcat-embed-el + ${tomcat.version} + + + org.apache.tomcat.embed + tomcat-embed-jasper + ${tomcat.version} org.nutz @@ -186,6 +183,7 @@ 4.5.0 1.15.3 8.0.28 + 9.0.121 1.25.0 1.6.2 4.0.0 @@ -197,7 +195,6 @@ 3.8.1 UTF-8 2.5.1 - 9.4.57.v20241219-cug-patch1 2.6.0-SNAPSHOT 1.5.8 1.r.70-SNAPSHOT diff --git a/pom.xml b/pom.xml index 88c48fe2..b74b4aa7 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 2.6.0-SNAPSHOT 1.r.70-SNAPSHOT - 9.4.57.v20241219-cug-patch1 + 9.0.121 8.0.28 2.3.1 2.0.8 @@ -145,8 +145,22 @@ org.nutz - nutzboot-starter-jetty + nutzboot-starter-tomcat ${nutzboot.version} + + + org.gandon.tomcat + juli-to-slf4j + + + + + org.apache.tomcat.embed + tomcat-embed-websocket + + + org.nutz + nutz-plugins-websocket org.nutz @@ -425,31 +439,39 @@ - + - org.eclipse.jetty - jetty-http - ${jetty.cug-patch.version} + javax.servlet + javax.servlet-api + 4.0.1 + provided - org.eclipse.jetty - jetty-server - ${jetty.cug-patch.version} + javax.websocket + javax.websocket-api + 1.1 + provided + + + + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} - org.eclipse.jetty - jetty-security - ${jetty.cug-patch.version} + org.apache.tomcat.embed + tomcat-embed-websocket + ${tomcat.version} - org.eclipse.jetty - jetty-client - ${jetty.cug-patch.version} + org.apache.tomcat.embed + tomcat-embed-el + ${tomcat.version} - org.eclipse.jetty - jetty-jaspi - ${jetty.cug-patch.version} + org.apache.tomcat.embed + tomcat-embed-jasper + ${tomcat.version} org.nutz @@ -543,17 +565,6 @@ - - cug-jetty-patch - file:///${project.basedir}/tools/jetty-security-patch/repository - - true - fail - - - false - - nutz https://jfrog.nutz.cn/artifactory/libs-release diff --git a/src/main/java/com/budwk/app/MainLauncher.java b/src/main/java/com/budwk/app/MainLauncher.java index 07e198c0..8d088b14 100644 --- a/src/main/java/com/budwk/app/MainLauncher.java +++ b/src/main/java/com/budwk/app/MainLauncher.java @@ -11,6 +11,7 @@ import com.budwk.app.web.commons.auth.satoken.SaTokenDaoRedisImpl; import com.budwk.app.web.commons.auth.satoken.StpInterfaceImpl; import com.budwk.app.web.commons.base.Globals; import com.budwk.app.web.commons.ext.pubsub.WebPubSub; +import com.budwk.app.web.commons.ext.handler.WkTomcatConfiguration; import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService; import lombok.extern.slf4j.Slf4j; import org.beetl.core.GroupTemplate; @@ -65,6 +66,7 @@ public class MainLauncher { public static void main(String[] args) throws Exception { NbApp nb = new NbApp().setArgs(args).setPrintProcDoc(true); nb.getAppContext().setMainPackage("com.budwk"); + nb.addListener(new WkTomcatConfiguration()); nb.run(); } diff --git a/src/main/java/com/budwk/app/web/commons/ext/handler/WkErrorPageHandler.java b/src/main/java/com/budwk/app/web/commons/ext/handler/WkErrorPageHandler.java index 0090bab6..bb2e682f 100644 --- a/src/main/java/com/budwk/app/web/commons/ext/handler/WkErrorPageHandler.java +++ b/src/main/java/com/budwk/app/web/commons/ext/handler/WkErrorPageHandler.java @@ -1,7 +1,6 @@ package com.budwk.app.web.commons.ext.handler; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.servlet.ErrorPageErrorHandler; +import org.nutz.boot.starter.WebServletFace; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.json.Json; import org.nutz.lang.util.NutMap; @@ -9,28 +8,63 @@ import org.nutz.log.Log; import org.nutz.log.Logs; import javax.servlet.RequestDispatcher; +import javax.servlet.DispatcherType; +import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServlet; import java.io.IOException; /** * 错误页拦截器,登陆后台显示友好提示 */ @IocBean -public class WkErrorPageHandler extends ErrorPageErrorHandler { +public class WkErrorPageHandler extends HttpServlet implements WebServletFace { private static final Log log = Logs.get(); + /** + * 返回容器错误处理 Servlet 的注册名称。 + * @return 独立于业务路由的 Servlet 名称 + */ @Override - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + public String getName() { + return "wkErrorPage"; + } + + /** + * 返回容器错误页的内部派发地址。 + * @return 错误页映射路径 + */ + @Override + public String getPathSpec() { + return "/__container_error"; + } + + /** + * 将当前错误处理实例交给 NutzBoot 注册。 + * @return 当前 Servlet 实例 + */ + @Override + public Servlet getServlet() { + return this; + } + + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + if (request.getDispatcherType() != DispatcherType.ERROR) { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } if (response.getStatus() == 403 || response.getStatus() == 404 || response.getStatus() == 500) { try { if (isAjax(request)) { + response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(Json.toJson(new NutMap("code", "-1").setv("msg", response.getStatus() + " error"))); return; } else { - request.setAttribute("original_request_uri", request.getRequestURI()); + request.setAttribute("original_request_uri", request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI)); RequestDispatcher rd = request.getRequestDispatcher("/platform/home/" + response.getStatus()); rd.forward(request, response); return; @@ -39,11 +73,11 @@ public class WkErrorPageHandler extends ErrorPageErrorHandler { log.error(e); } } - super.handle(target, baseRequest, request, response); + // 不回落到容器默认错误页,避免泄露异常堆栈和容器信息。 } private boolean isAjax(ServletRequest req) { String value = ((HttpServletRequest) req).getHeader("X-Requested-With"); return value != null && "XMLHttpRequest".equalsIgnoreCase(value.trim()); } -} \ No newline at end of file +} diff --git a/src/main/java/com/budwk/app/web/commons/ext/handler/WkTomcatConfiguration.java b/src/main/java/com/budwk/app/web/commons/ext/handler/WkTomcatConfiguration.java new file mode 100644 index 00000000..67fed5c6 --- /dev/null +++ b/src/main/java/com/budwk/app/web/commons/ext/handler/WkTomcatConfiguration.java @@ -0,0 +1,88 @@ +package com.budwk.app.web.commons.ext.handler; + +import com.budwk.app.web.commons.ext.websocket.WkWebSocket; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.Executor; +import org.apache.catalina.core.StandardThreadExecutor; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.valves.ErrorReportValve; +import org.apache.tomcat.websocket.server.WsSci; +import org.apache.tomcat.util.descriptor.web.FilterMap; +import org.nutz.boot.NbApp; +import org.nutz.boot.starter.tomcat.TomcatStarter; +import org.nutz.boot.tools.NbAppEventListener; +import org.nutz.ioc.impl.PropertiesProxy; + +import java.util.Set; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +/** + * 在容器启动前补齐 NutzBoot Tomcat 的 WebSocket 注册和线程池绑定。 + */ +public class WkTomcatConfiguration implements NbAppEventListener { + + /** + * 配置官方嵌入式 Tomcat,保留项目的监听地址与消息推送能力。 + * + * @param app 当前 NutzBoot 应用 + * @param eventType 只在服务器启动前配置,避免重复注册端点 + */ + @Override + public void whenStartServers(NbApp app, EventType eventType) { + if (eventType != EventType.before) { + return; + } + TomcatStarter starter = app.getAppContext().getIoc().get(TomcatStarter.class); + Tomcat tomcat = starter.getServer(); + PropertiesProxy conf = app.getAppContext().getConf(); + tomcat.getConnector().setProperty("address", starter.getHost()); + + // Starter 创建了共享线程池,但尚未把它交给 Connector 使用。 + for (Executor executor : tomcat.getService().findExecutors()) { + if (executor instanceof StandardThreadExecutor) { + StandardThreadExecutor threads = (StandardThreadExecutor) executor; + threads.setMinSpareThreads(conf.getInt("tomcat.executor.minSpareThreads", 10)); + threads.setMaxIdleTime(conf.getInt("tomcat.executor.maxIdleTime", 60000)); + tomcat.getConnector().getProtocolHandler().setExecutor(threads); + break; + } + } + + // NutzBoot 使用编程式 Context,必须显式运行官方 WebSocket 初始化器。 + StandardContext context = (StandardContext) tomcat.getHost().findChild(starter.getContextPath()); + context.setUseHttpOnly(conf.getBoolean("tomcat.session.cookie.httponly", true)); + context.addServletContainerInitializer(new WsSci(), Set.of(WkWebSocket.class)); + // Tomcat 的错误页再次 forward 时仍保持 ERROR 派发;让 MVC 渲染原业务错误页。 + // Nutz 的过滤器在前一个监听器中注册,须等其完成后才能添加映射。 + context.addApplicationLifecycleListener(new ServletContextListener() { + /** + * 在 Nutz 注册完成后补充业务错误页的 ERROR 派发映射。 + * @param event 当前 Servlet 上下文初始化事件 + */ + @Override + public void contextInitialized(ServletContextEvent event) { + FilterMap errorPages = new FilterMap(); + errorPages.setFilterName("nutz"); + errorPages.setDispatcher("ERROR"); + // ERROR 再转发时 URL 匹配路径不变,按目标 Servlet 匹配才能进入 MVC。 + errorPages.addServletName("default"); + context.addFilterMap(errorPages); + } + }); + // Host 的兜底错误页也不能重新暴露此前已关闭的版本和堆栈。 + ErrorReportValve errorReport = null; + for (org.apache.catalina.Valve valve : tomcat.getHost().getPipeline().getValves()) { + if (valve instanceof ErrorReportValve) { + errorReport = (ErrorReportValve) valve; + break; + } + } + if (errorReport == null) { + errorReport = new ErrorReportValve(); + tomcat.getHost().getPipeline().addValve(errorReport); + } + errorReport.setShowReport(false); + errorReport.setShowServerInfo(false); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/mall/models/entity/goods/MallGoods.java b/src/main/java/com/budwk/app/zhgh/mall/models/entity/goods/MallGoods.java index a9f440e1..1595db53 100644 --- a/src/main/java/com/budwk/app/zhgh/mall/models/entity/goods/MallGoods.java +++ b/src/main/java/com/budwk/app/zhgh/mall/models/entity/goods/MallGoods.java @@ -11,7 +11,6 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import org.eclipse.jetty.util.StringUtil; import org.nutz.dao.entity.annotation.*; import org.nutz.dao.interceptor.annotation.PrevInsert; @@ -161,13 +160,13 @@ public class MallGoods extends BaseModel { //循环sku,判定sku是否有效 for (Map sku : goodsOperationDTO.getSkuList()) { //判定参数不能为空 - if ((!sku.containsKey("price") || StringUtil.isEmpty(sku.get("price").toString()) || Convert.toDouble(sku.get("price")) <= 0)) { + if ((!sku.containsKey("price") || CharSequenceUtil.isEmpty(sku.get("price").toString()) || Convert.toDouble(sku.get("price")) <= 0)) { throw new BaseException("商品SKU价格不能小于等于0"); } // if ((!sku.containsKey("cost") || StringUtil.isEmpty(sku.get("cost").toString()) || Convert.toDouble(sku.get("cost")) <= 0)) { // throw new BaseException("商品SKU成本价不能小于等于0"); // } - if (!sku.containsKey("quantity") || StringUtil.isEmpty(sku.get("quantity").toString()) || Convert.toInt(sku.get("quantity").toString()) < 0) { + if (!sku.containsKey("quantity") || CharSequenceUtil.isEmpty(sku.get("quantity").toString()) || Convert.toInt(sku.get("quantity").toString()) < 0) { throw new BaseException("商品库存数量不能为负数"); } sku.values().forEach(i -> { diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml index 3c83585d..57bcf154 100644 --- a/src/main/resources/application-dev.yaml +++ b/src/main/resources/application-dev.yaml @@ -16,24 +16,20 @@ sm4cbc: keyHex: 00112233445566778899aabbccddeeff ivHex: 0102030405060708090a0b0c0d0e0f10 -jetty: - contextPath: / - # HTTP 与 HTTPS 均隐藏容器版本;安全漏洞仍需通过维护补丁修复。 - httpConfig: - sendServerVersion: false - httpsConfig: - sendServerVersion: false +tomcat: + contextPath: "" + # 容器错误响应不披露版本;Session Cookie 保持 HttpOnly。 session: cookie: httponly: true - threadpool: - idleTimeout: 60000 - minThreads: 10 + executor: + maxIdleTime: 60000 + minSpareThreads: 10 maxThreads: 500 page: - 403: /error/403.html - 404: /error/404.html - 500: /error/500.html + 403: /__container_error + 404: /__container_error + 500: /__container_error #结合ftp使用,或用nginx代理ftp路径 #staticPath: /Users/wizzer/temp/files #开发模式静态资源