这里有一些需要注意的地方...禁用TRACE请求,并从OPTIONS响应头中删除TRACE。
首先,让我们解决简单的问题,禁用TRACE请求。
这可以通过在ServletContext级别上进行约束映射来完成。
这将影响所有Servlet端点上TRACE请求的使用。
请参见下面的示例。
接下来,从OPTIONS Allow
头字段中删除TRACE。
这不像禁用TRACE那样容易以全局方式完成。
这是因为每个HttpServlet
都有一个默认实现的doOptions(HttpServletRequest, HttpServletResponse)
,它将始终添加TRACE和OPTIONS到Allow
头中,以及您已经为该特定HttpServlet
端点启用的任何其他方法。
您可以在此处查看HttpServlet.doOption
的实际默认实现...
https://github.com/eclipse-ee4j/servlet-api/blob/4.0.4-RELEASE/api/src/main/java/javax/servlet/http/HttpServlet.java#L361-L432
为什么OPTIONS方法请求总是返回TRACE和OPTIONS?
这是因为在HttpServlet中这些方法的默认实现总是返回响应,所以它们总是被添加到Allow头中。
这意味着OPTIONS是在每个servlet范围内受控制的。
您可以在特定的servlet中覆盖doOptions方法并使其返回您想要的内容,但您不能有一个顶级的约束或过滤器来控制它,因为约束或过滤器都不知道Servlet端点的详细信息,无法正确确定OPTIONS Allow头的其他部分(如GET、HEAD、POST、PUT等)。
以下是一些示例代码,展示了正确的约束映射和可选的doOption覆盖。
package jetty;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.security.Constraint;
import static java.nio.charset.StandardCharsets.UTF_8;
public class NoTraceDemo
{
public static void main(String[] args)
{
Server server = new Server(8888);
ServletContextHandler servletContextHandler = new ServletContextHandler();
servletContextHandler.addServlet(HelloServlet.class, "/");
ConstraintSecurityHandler constraintSecurityHandler = new ConstraintSecurityHandler();
servletContextHandler.setSecurityHandler(constraintSecurityHandler);
Constraint constraintDisableTrace = new Constraint();
constraintDisableTrace.setAuthenticate(true);
ConstraintMapping mappingDisableTrace = new ConstraintMapping();
mappingDisableTrace.setPathSpec("/");
mappingDisableTrace.setMethod("TRACE");
mappingDisableTrace.setConstraint(constraintDisableTrace);
constraintSecurityHandler.addConstraintMapping(mappingDisableTrace);
Constraint constraintEnabledEverythingButTrace = new Constraint();
ConstraintMapping mappingEnableEverythingButTrace = new ConstraintMapping();
mappingEnableEverythingButTrace.setPathSpec("/");
mappingEnableEverythingButTrace.setMethodOmissions(new String[]{"TRACE"});
mappingEnableEverythingButTrace.setConstraint(constraintEnabledEverythingButTrace);
constraintSecurityHandler.addConstraintMapping(mappingEnableEverythingButTrace);
server.setHandler(servletContextHandler);
try
{
server.start();
URL url = server.getURI().toURL();
request("TRACE", url);
request("OPTIONS", url);
request("GET", url);
}
catch (Throwable t)
{
t.printStackTrace();
}
finally
{
LifeCycle.stop(server);
}
}
private static void request(String method, URL url)
{
try
{
HttpURLConnection http = (HttpURLConnection)url.openConnection();
http.setDoOutput(true);
http.setRequestMethod(method);
System.out.println("---------");
System.out.printf("%s %s HTTP/1.1%n", http.getRequestMethod(), http.getURL());
System.out.println("----");
System.out.printf("%s%n", http.getHeaderField(null));
http.getHeaderFields().entrySet().stream()
.filter(entry -> entry.getKey() != null)
.forEach((entry) -> System.out.printf("%s: %s%n", entry.getKey(), http.getHeaderField(entry.getKey())));
InputStream bodyStream = (http.getResponseCode() == 200) ? http.getInputStream() : http.getErrorStream();
if (bodyStream != null)
{
String body = IO.toString(bodyStream, UTF_8);
System.out.println(body);
}
}
catch (Throwable t)
{
t.printStackTrace(System.out);
}
}
public static class HelloServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/plain");
resp.getWriter().printf("Hello from %s%n", this.getClass().getName());
}
}
}
您可以随时将HttpServlet.doOptions
方法复制/粘贴到自己的servlet中,然后删除或更改TRACE行为默认值。