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
        }

    }
相关推荐
LB211216 分钟前
Redis 黑马skyout
java·数据库·redis
豐儀麟阁贵22 分钟前
Java知识点储备
java·开发语言
hrrrrb29 分钟前
【Spring Security】Spring Security 密码编辑器
java·hive·spring
豐儀麟阁贵32 分钟前
2.3变量与常量
java·开发语言
兮动人2 小时前
Eureka注册中心通用写法和配置
java·云原生·eureka
爱编程的小白L4 小时前
基于springboot志愿服务管理系统设计与实现(附源码)
java·spring boot·后端
聪明的笨猪猪6 小时前
Java Redis “持久化”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
聪明的笨猪猪6 小时前
Java Redis “核心基础”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
奋斗的小monkey8 小时前
Spring Boot 3.x核心特性与性能优化实战
java·spring boot·微服务·性能优化·响应式编程