java.lang.IllegalArgumentException: 在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义

Tomcat 屏蔽错误信息。java.lang.IllegalArgumentException: 在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义

cpp 复制代码
<h1>HTTP状态 400 - 错误的请求</h1>
	<hr class="line" />
	<p><b>类型</b> 异常报告</p>
	<p><b>消息</b> 在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义</p>
	<p><b>描述</b> 由于被认为是客户端对错误(例如:畸形的请求语法、无效的请求信息帧或者虚拟的请求路由),服务器无法或不会处理当前请求。</p>
	<p><b>例外情况</b></p>
	<pre>java.lang.IllegalArgumentException: 在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义
	org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:512)
	org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:503)
	org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:831)
	org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1631)
	org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	java.lang.Thread.run(Thread.java:750)

简介

在开发和生产环境中,出于安全和隐私的考虑,我们可能不希望将详细的错误信息暴露给用户。Tomcat 提供了一种机制,允许我们通过配置 ErrorReportValve 来控制错误报告的显示。本文将详细介绍如何在 Tomcat 的 server.xml 文件中配置 ErrorReportValve,以屏蔽错误报告和服务器信息。

为什么需要屏蔽错误报告?

  1. 安全考虑:错误报告可能包含敏感信息,如数据库连接字符串、系统路径等,这些信息如果被恶意用户获取,可能会对系统安全造成威胁。

  2. 用户体验:对于最终用户来说,看到详细的错误信息可能会引起困惑或不安。提供一个友好的错误页面可以改善用户体验。

  3. 避免信息泄露:在生产环境中,错误报告可能会泄露系统的内部工作原理,这可能会被恶意用户利用。

如何配置 Tomcat 屏蔽错误报告

步骤 1:打开 server.xml 文件

server.xml 文件通常位于 Tomcat 的 conf 目录下。使用文本编辑器打开此文件。

步骤 2:添加 ErrorReportValve

<Host> 标签内添加 <Valve> 标签,并设置 classNameorg.apache.catalina.valves.ErrorReportValve,同时将 showReportshowServerInfo 设置为 false。如下所示:

xml复制

<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
    <!-- 其他配置 -->
    <Valve className="org.apache.catalina.valves.ErrorReportValve" showReport="false" showServerInfo="false"/>
</Host>

步骤 3:保存并重启 Tomcat

保存 server.xml 文件后,重启 Tomcat 以使更改生效。

步骤 4:测试配置

为了验证配置是否生效,可以尝试访问一个不存在的页面或故意引发一个错误。Tomcat 应该不再显示详细的错误报告和服务器信息。

注意事项

  1. 调试问题:在开发环境中,关闭错误报告可能会使调试问题变得更加困难。因此,建议仅在生产环境中关闭错误报告。

  2. 日志记录:虽然错误报告被屏蔽了,但错误信息仍然会记录在 Tomcat 的日志文件中。确保你的日志记录策略能够满足故障排查的需求。

  3. 自定义错误页面:为了提供更好的用户体验,你可以配置自定义的错误页面,当发生错误时,引导用户到这些页面。

结论

通过在 Tomcat 中配置 ErrorReportValve,我们可以有效地屏蔽错误报告和服务器信息,从而提高系统的安全性和用户体验。然而,这也意味着我们需要更加依赖日志文件来进行故障排查,因此建立一个有效的日志记录和监控策略是非常重要的。

##tomcat-embed-core-8.5.72-sources.jar!/org/apache/catalina/valves/ErrorReportValve.java

源码

isShowReport() 展示错误堆栈

isShowServerInfo() 展示版本信息

cpp 复制代码
protected void report(Request request, Response response, Throwable throwable) {

        int statusCode = response.getStatus();

        // Do nothing on a 1xx, 2xx and 3xx status
        // Do nothing if anything has been written already
        // Do nothing if the response hasn't been explicitly marked as in error
        //    and that error has not been reported.
        if (statusCode < 400 || response.getContentWritten() > 0 || !response.setErrorReported()) {
            return;
        }

        // If an error has occurred that prevents further I/O, don't waste time
        // producing an error report that will never be read
        AtomicBoolean result = new AtomicBoolean(false);
        response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result);
        if (!result.get()) {
            return;
        }

        String message = Escape.htmlElementContent(response.getMessage());
        if (message == null) {
            if (throwable != null) {
                String exceptionMessage = throwable.getMessage();
                if (exceptionMessage != null && exceptionMessage.length() > 0) {
                    try (Scanner scanner = new Scanner(exceptionMessage)) {
                        message = Escape.htmlElementContent(scanner.nextLine());
                    }
                }
            }
            if (message == null) {
                message = "";
            }
        }

        // Do nothing if there is no reason phrase for the specified status code and
        // no error message provided
        String reason = null;
        String description = null;
        StringManager smClient = StringManager.getManager(
                Constants.Package, request.getLocales());
        response.setLocale(smClient.getLocale());
        try {
            reason = smClient.getString("http." + statusCode + ".reason");
            description = smClient.getString("http." + statusCode + ".desc");
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
        }
        if (reason == null || description == null) {
            if (message.isEmpty()) {
                return;
            } else {
                reason = smClient.getString("errorReportValve.unknownReason");
                description = smClient.getString("errorReportValve.noDescription");
            }
        }

        StringBuilder sb = new StringBuilder();

        sb.append("<!doctype html><html lang=\"");
        sb.append(smClient.getLocale().getLanguage()).append("\">");
        sb.append("<head>");
        sb.append("<title>");
        sb.append(smClient.getString("errorReportValve.statusHeader",
                String.valueOf(statusCode), reason));
        sb.append("</title>");
        sb.append("<style type=\"text/css\">");
        sb.append(TomcatCSS.TOMCAT_CSS);
        sb.append("</style>");
        sb.append("</head><body>");
        sb.append("<h1>");
        sb.append(smClient.getString("errorReportValve.statusHeader",
                String.valueOf(statusCode), reason)).append("</h1>");
        if (isShowReport()) {
            sb.append("<hr class=\"line\" />");
            sb.append("<p><b>");
            sb.append(smClient.getString("errorReportValve.type"));
            sb.append("</b> ");
            if (throwable != null) {
                sb.append(smClient.getString("errorReportValve.exceptionReport"));
            } else {
                sb.append(smClient.getString("errorReportValve.statusReport"));
            }
            sb.append("</p>");
            if (!message.isEmpty()) {
                sb.append("<p><b>");
                sb.append(smClient.getString("errorReportValve.message"));
                sb.append("</b> ");
                sb.append(message).append("</p>");
            }
            sb.append("<p><b>");
            sb.append(smClient.getString("errorReportValve.description"));
            sb.append("</b> ");
            sb.append(description);
            sb.append("</p>");
            if (throwable != null) {
                String stackTrace = getPartialServletStackTrace(throwable);
                sb.append("<p><b>");
                sb.append(smClient.getString("errorReportValve.exception"));
                sb.append("</b></p><pre>");
                sb.append(Escape.htmlElementContent(stackTrace));
                sb.append("</pre>");

                int loops = 0;
                Throwable rootCause = throwable.getCause();
                while (rootCause != null && (loops < 10)) {
                    stackTrace = getPartialServletStackTrace(rootCause);
                    sb.append("<p><b>");
                    sb.append(smClient.getString("errorReportValve.rootCause"));
                    sb.append("</b></p><pre>");
                    sb.append(Escape.htmlElementContent(stackTrace));
                    sb.append("</pre>");
                    // In case root cause is somehow heavily nested
                    rootCause = rootCause.getCause();
                    loops++;
                }

                sb.append("<p><b>");
                sb.append(smClient.getString("errorReportValve.note"));
                sb.append("</b> ");
                sb.append(smClient.getString("errorReportValve.rootCauseInLogs"));
                sb.append("</p>");

            }
            sb.append("<hr class=\"line\" />");
        }
        if (isShowServerInfo()) {
            sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
        }
        sb.append("</body></html>");

        try {
            try {
                response.setContentType("text/html");
                response.setCharacterEncoding("utf-8");
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                if (container.getLogger().isDebugEnabled()) {
                    container.getLogger().debug("status.setContentType", t);
                }
            }
            Writer writer = response.getReporter();
            if (writer != null) {
                // If writer is null, it's an indication that the response has
                // been hard committed already, which should never happen
                writer.write(sb.toString());
                response.finishResponse();
            }
        } catch (IOException | IllegalStateException e) {
            // Ignore
        }

    }
相关推荐
shuair1 小时前
idea 2023.3.7常用插件
java·ide·intellij-idea
小安同学iter1 小时前
使用Maven将Web应用打包并部署到Tomcat服务器运行
java·tomcat·maven
Yvonne9781 小时前
创建三个节点
java·大数据
不会飞的小龙人2 小时前
Kafka消息服务之Java工具类
java·kafka·消息队列·mq
是小崔啊3 小时前
java网络编程02 - HTTP、HTTPS详解
java·网络·http
brevity_souls3 小时前
Spring Boot 内置工具类
java·spring boot
小钊(求职中)4 小时前
Java开发实习面试笔试题(含答案)
java·开发语言·spring boot·spring·面试·tomcat·maven
shix .4 小时前
什么是tomcat
java·tomcat
java技术小馆4 小时前
Deepseek整合SpringAI
java·spring cloud
天荒地老笑话么4 小时前
Mac安装配置Tomcat 8
java·macos·tomcat