Spring Boot:从 HTTP 重定向到 HTTPS 导致 PUT 方法出现 405 错误

问题描述:

我有一个与此非常相似的问题:重定向 Post 方法 HTTP ->HTTPS - HTTP 状态 405(春季启动)

I have a problem very similar to this one: Redirect Post method HTTP -> HTTPS - HTTP Status 405 (Spring boot)

基本上,我试图通过从 HTTP 到 HTTPS 的重定向让 Spring Boot 同时提供 HTTP 和 HTTPS.它有效,但仅适用于 GET 请求.如果我执行 PUT 请求,我会收到不支持请求方法‘GET’"错误,所以看起来我的 PUT 请求正在以某种方式转换为 GET 请求.

Basically, I'm trying to make Spring Boot serve both HTTP and HTTPS with the redirection from HTTP to HTTPS. It works, but only for GET requests. If I perform PUT request, I get "Request method 'GET' not supported" error, so looks like my PUT request is being converted to GET request somewhere somehow.

我尝试了两种配置此类重定向的方法:在 application.properties 中定义 HTTPS 连接,然后以编程方式添加 HTTP,反之亦然.两者都不起作用.

I tried both ways of configuring such redirect: define HTTPS connectivity in application.properties and then add HTTP programmatically and vise versa. Neither is working.

这是第一种方法:

@Bean
public EmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
    TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory() {
        @Override
        protected void postProcessContext(Context context) {
            SecurityConstraint securityConstraint = new SecurityConstraint();
            securityConstraint.setUserConstraint("CONFIDENTIAL");
            SecurityCollection collection = new SecurityCollection();
            collection.addPattern("/*");
            securityConstraint.addCollection(collection);
            context.addConstraint(securityConstraint);
        }
    };
    addHTTPConnector(factory);
    return factory;
}

private void addHTTPConnector(TomcatEmbeddedServletContainerFactory factory) {
    Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);
    connector.setScheme("http");
    connector.setPort(8080);
    connector.setRedirectPort(8443);
    connector.setSecure(false);
    Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
    protocol.setSSLEnabled(false);
    factory.addAdditionalTomcatConnectors(connector);
}

使用application.properties:

server.port=8443
server.ssl.key-store=keystore.p12
server.ssl.key-store-password=password
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=alias

这是第二种方法:

@Bean
public EmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
    TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory() {
        @Override
        protected void postProcessContext(Context context) {
            SecurityConstraint securityConstraint = new SecurityConstraint();
            securityConstraint.setUserConstraint("CONFIDENTIAL");
            SecurityCollection collection = new SecurityCollection();
            collection.addPattern("/*");
            securityConstraint.addCollection(collection);
            context.addConstraint(securityConstraint);
        }
    };
    addHTTPSConnector(factory);
    factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> connector.setRedirectPort(8443));
    return factory;
}

private void addHTTPSConnector(TomcatEmbeddedServletContainerFactory factory) {
    Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);
    connector.setScheme("https");
    connector.setPort(8443);
    connector.setSecure(true);
    Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
    protocol.setSSLEnabled(true);
    protocol.setKeystoreFile("keystore.p12");
    protocol.setKeystorePass("password");
    protocol.setKeystoreType("pkcs12");
    protocol.setKeystoreProvider("SunJSSE");
    protocol.setKeyAlias("alias");
    factory.addAdditionalTomcatConnectors(connector);
}

我也有

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requiresChannel().anyRequest().requiresSecure();
    }

}

为了使重定向工作.

我也看到了这个答案:Spring Boot不支持请求方法‘GET’"通过 Catalina 连接器将 POST 请求重定向到 https 端口

但我不知道什么是DEFAULT_PROTOCOL"常量.我尝试在那里添加所有方法(POST、PUT、DELETE、GET 等),但没有帮助.

But I don't know what is "DEFAULT_PROTOCOL" constant. I tried adding all the methods there (POST, PUT, DELETE, GET, etc.), but it didn't help.

重定向是专门通知客户端(例如 Web 浏览器)使用给定的 URL 执行 GET 请求,因此结果重定向不能是 PUTPOSTDELETE 或任何其他 HTTP 方法.

A redirect is specifically to inform the client (e.g. web browser) to do a GET request using a given URL, so the result of a redirect cannot be a PUT, POST, DELETE, or any other HTTP method.

在这种情况下,重定向到 HTTPS 的主要目的是保护连接免受窥探,即确保没有人可以看到机密信息.这适用于 GET,因为您还没有发送机密信息1,假设它是包含机密信息的响应.

In this context, the main purpose of redirecting to HTTPS is to secure the connection from snooping, i.e. ensure that no one can see confidential information. This works well for a GET, since you haven't sent confidential information yet1, assuming it is the response that contains confidential information.

PUTPOST 重定向到 HTTPS 是没有意义的,因为您已经通过不安全的连接发送了有效负载(机密数据).

Redirecting a PUT or a POST to HTTPS is meaningless, since you already sent the payload (the confidential data) over an unsecure connection.

您的客户端需要被告知在发送数据之前使用 HTTPS ,即当它构建 PUT/POST 请求时,它需要提供 HTTPS 网址.

Your client needs to be told to use HTTPS before it sends the data, i.e. when it builds the PUT / POST request, it needs to be given an HTTPS URL.

修复客户端代码,例如生成 HTTP PUT 的 JavaScript 代码,因此它使用 HTTPS.重定向为时已晚,而且完全错误.

Fix the client code, e.g. the JavaScript code that generates the HTTP PUT, so it uses HTTPS. Redirecting is too late, and entirely wrong.

PUT 的重定向失败实际上是一件好事,因为它迫使您正确保护您的 Web 应用程序.如果它没有失败,您就会错误地认为您的 Web 应用程序受到重定向的保护,而实际上并非如此.

It is actually a good thing that redirect of PUT failed, because it forces you to correctly secure your web application. If it hadn't failed, you'd mistakenly have thought that you web application was secured by the redirect, when in fact it wasn't.

1) GET 也可以包含机密信息,例如在查询字符串中.如果是这样,它就不应该使用 HTTP 发送,因此在这种情况下,保护 PUT/POST 的规则也适用于 GET.

1) The GET can contain confidential information too, e.g. in the query string. If it does, it should never have been sent using HTTP, so rules for securing PUT / POST also applies to GET in such cases.