Spring Boot 2.7.0内嵌Jetty开启目录列表访问/foo返回404问题
Spring Boot 2.7.0内嵌Jetty配置目录列表访问/foo返回404问题
问题背景
在Spring Boot v2.7.0版本中使用内嵌Jetty,尝试实现WebServerFactoryCustomizer接口开启目录列表功能,初始实现代码如下:
public class MyJettyWebServerCustomizer implements WebServerFactoryCustomizer<JettyServletWebServerFactory> { @Override public void customize(JettyServletWebServerFactory factory) { JettyServerCustomizer jettyServerCustomizer = new JettyServerCustomizer() { @Override public void customize(Server server) { Handler[] childHandlersByClass = server.getChildHandlersByClass(WebAppContext.class); final WebAppContext webAppContext = (WebAppContext) childHandlersByClass[0]; ServletHandler handler = webAppContext.getServletHandler(); ServletHolder defServlet = ((WebAppContext)server.getHandlers()[0]).getServletHandler().getServlets()[0]; defServlet.setInitParameter("dirAllowed","true"); defServlet.setInitParameter("resourceBase","/tmp/"); webAppContext.getServletHandler().addServletWithMapping(defServlet, "/foo"); webAppContext.getServletHandler().getServletMapping("/foo").setDefault(true); } }; factory.addServerCustomizers(jettyServerCustomizer); } }
访问http://localhost:8080/foo时返回404状态码。后续尝试直接新建Default Servlet的方案排查,问题依旧存在,对应代码片段如下:
ServletHolder holder = new ServletHolder(); holder.setName("default"); holder.setClassName("org.eclipse.jetty.servlet.DefaultServlet"); holder.setInitParameter("dirAllowed", "true"); holder.setInitOrder(1); webAppContext.getServletHandler().addServletWithMapping(holder, "/foo"); webAppContext.getServletHandler().getServletMapping("/foo").setDefault(true);
疑问为该问题是对底层配置逻辑存在理解偏差,还是遗漏了必要配置项。
问题根因
实现存在3个核心错误,属于对Jetty DefaultServlet加载逻辑和映射规则的认知偏差:
- 复用已初始化的DefaultServlet修改参数无效:Jetty的Servlet初始化参数仅在Servlet首次启动加载时读取,拿到容器内置的DefaultServlet时,它已经完成初始化,后续再调用
setInitParameter修改dirAllowed、resourceBase不会产生任何效果。 - 映射规则配置错误:将路径写为
/foo是精确匹配规则,仅会匹配/foo这一个请求路径,子路径资源访问、目录遍历逻辑都不会触发,需要配置为前缀匹配/foo/*才能覆盖路径下所有资源。 - 缺少关键参数
pathInfoOnly=true:没有这个参数时,DefaultServlet会把完整请求路径拼接到资源查找路径后,会去查找/tmp/foo目录而非/tmp目录,路径不存在自然返回404。另外给自定义映射设置default=true会覆盖容器默认的DefaultServlet,会导致原有静态资源、错误页处理逻辑失效。 - 额外注意:不要给自定义DefaultServlet设置
name="default",这个名称是Jetty内置默认Servlet的保留名称,重名会导致覆盖原有Servlet配置,引发不可预期的问题。
正确实现代码
public class MyJettyWebServerCustomizer implements WebServerFactoryCustomizer<JettyServletWebServerFactory> { @Override public void customize(JettyServletWebServerFactory factory) { factory.addServerCustomizers(server -> { WebAppContext webAppContext = (WebAppContext) server.getChildHandlersByClass(WebAppContext.class)[0]; // 新建独立的DefaultServlet实例,不要复用容器已初始化的默认Servlet ServletHolder tmpDirServlet = new ServletHolder("tmp-dir-list", DefaultServlet.class); tmpDirServlet.setInitParameter("dirAllowed", "true"); // 配置为仅处理路径后缀部分,避免把/foo拼接到资源路径后 tmpDirServlet.setInitParameter("pathInfoOnly", "true"); tmpDirServlet.setInitParameter("resourceBase", "/tmp/"); tmpDirServlet.setInitOrder(0); // 使用前缀匹配规则映射路径 webAppContext.addServlet(tmpDirServlet, "/foo/*"); }); } }
配置完成后启动服务,访问http://localhost:8080/foo即可看到/tmp目录下的文件列表,访问/foo/xxx会自动映射到/tmp/xxx对应的文件或子目录。
注意:请确保Jetty运行进程对配置的
/tmp目录有读取和遍历权限,Windows环境下路径需要调整为系统对应格式,比如C:/tmp/,使用正斜杠避免转义问题。
内容的提问来源于stack exchange,提问作者Stefan Ziegler
相关产品推荐
相关产品推荐

