Spring Boot: redirect from HTTP to HTTPS results in 405 error for PUT method

狂风中的少年 提交于 2020-08-07 04:19:26

问题


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

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.

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.

Here's the first approach:

@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);
}

With 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

Here's the second approach:

@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);
}

For both I also have

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

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

}

in order to make redirect work.

I have also seen this answer: Spring Boot "Request method 'GET' not supported" while redirecting POST request to https port through Catalina Connector

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.


回答1:


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.

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.

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

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.

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.

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) 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.



来源:https://stackoverflow.com/questions/48629911/spring-boot-redirect-from-http-to-https-results-in-405-error-for-put-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!