如何在 URL 路径中使用双斜线时使用 Elastic Beanstalk (Apache+Tomcat) 解决 HTTP 404 错误,例如https://uat.myserver//rest/something

How to resolve HTTP 404 error with Elastic Beanstalk (Apache+Tomcat) when double-slash in URL path, e.g. https://uat.myserver//rest/something

我有一个托管在 Amazon Web Services 中的 Java Web 应用程序,它是 运行 在已退休的 Tomcat 8 with Amazon Linux 上 Linux。

我已尝试创建一个新环境来测试 运行 应用程序在最新的 Tomcat 8.5 和亚马逊上的 Corretto 11 运行 Linux 2.

在这个过程中我遇到了一个大问题,在我的应用程序中它使用了一个基 URL,例如https://myserver.com 并且它正在执行一些字符串连接以添加路径,例如/rest/myendpoint 当然应该解析为

https://myserver.com/rest/myendpoint

但不幸的是,它在不知不觉中通过了 C# 的 new URL(),它在连接之前附加了一个尾部斜杠。

所以

https://myserver.com/rest/myendpoint

作为

发送到服务器
https://myserver.com/rest//myendpoint

(注意 rest// 应该是 rest/

将 Java war 文件部署到新环境后,应用程序的一些请求失败并显示 404。我分析了这些网络日志,当然都是 URL 路径中有双重转义。

作为在 Postman 中编写请求以使单个斜杠工作正常的测试,双引号失败并返回 HTTP 404,就像应用程序一样。

现在的问题是这个应用已经在野外使用了,我们不能更改应用,因为这会涉及很长的延迟(构建,应用商店审批流程,然后等待大家升级应用程序),最有可能是几个月。

我在 AWS 门户中找不到任何东西来配置它。

我确实尝试切换到 Nginx,但我遇到了同样的错误。

我确实看到有人提到使用 .eb-extensions,但整个 apache 重写/mod 规则对我来说很陌生。实际上,我想将 http:// 或 https:// 之后的所有双斜杠重写为单斜杠。你能告诉我如何做到这一点吗?

我正在使用带 Tomcat 的 Elastic Beanstalk(目前支持 Apache)和最新的 Tomcat 平台版本 - Tomcat 8.5 和 Corretto 11 运行 64 位亚马逊 Linux 2.

谢谢。

因为我真的不想玩弄 .ebextensions,所以我扩展了一个现有的 servlet 过滤器(链中最早的过滤器)来检查路径。然后我就可以使用调度程序进行转发,而且效果很好。

虽然我确信我可以用 reg-ex 做一些更干净的事情,但这对我来说已经成功了,我可以先用 Postman 在本地测试它。

package com.devology.servlet.filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class AuthenticationFilter implements Filter {

    public AuthenticationFilter() {
    }

    private boolean doBeforeProcessing(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {

        HttpServletRequest hsr = (HttpServletRequest) request;
        final String path = hsr.getServletPath();
        String fullUrl = hsr.getRequestURL().toString();

        String remoteAddress = hsr.getRemoteAddr();

        // Example URL from bad app...
        //
        // http://localhost:8084/MyApp//rest/login
        //
        // path        =                                    /rest
        // fullUrl     = http://localhost:8084/MyApp//rest/login
        // indexOfPath =                                    ^ = 34
        // 
        // If the character before the start of the path is also a slash then
        // we need to internally forward
        int indexOfPath = fullUrl.indexOf(path + "/");
        boolean needsUrlRewrite = false;
        if (indexOfPath != -1) {
            String previousCharacter = fullUrl.substring(indexOfPath - 1, indexOfPath);
            needsUrlRewrite = "/".equals(previousCharacter);
        }
        if (needsUrlRewrite) {

            // Just take the path from the point of indexOfPath, .e.g /rest/login
            String rewrittenPath = fullUrl.substring(indexOfPath);
            RequestDispatcher dispatcher = request.getServletContext().getRequestDispatcher(rewrittenPath);
            dispatcher.forward(request, response);
            return false;
        }

        // Any other checks like authorisation, or return true to let all through
        return true;
    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain)
            throws IOException, ServletException {

        if (doBeforeProcessing(request, response)) {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void init(FilterConfig fc) throws ServletException {
        // Your Init
    }

    @Override
    public void destroy() {
        // Your cleanup
    }

}

这假设您在 web.xml 中像这样配置它...

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <filter>
        <filter-name>AuthenticationFilter</filter-name>
        <filter-class>com.devology.servlet.filters.AuthenticationFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AuthenticationFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspf</url-pattern>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>
    <error-page>
        <error-code>500</error-code>
        <location>/error-500.html</location>
    </error-page>
    <error-page>
        <error-code>404</error-code>
        <location>/error-404.html</location>
    </error-page>
</web-app>

我没有解决 .ebextensions 和 Apache 配置的问题,因为我真的不知道我在做什么,而上面的 servelet 过滤器完全满足了我的需要。