405 error on Webservices calls after migrating spr

2019-08-01 16:44发布

问题:

I am facing an unsolvable problem with my poor spring configuration knowledge: every GET return error 404 and every POST return error 405. My spring security filters work well but neither @PostMapping or @GetMapping annotated methods are getting called. I have taken care of rename old property server.context-path to new name server.servlet.context-path but it still not working. I use undertow webserver:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
    <version>${springBoot.version}</version>
</dependency>

My app.properties file is really named application.mssql.properties:

## Hibernate properties
... some properties ...

##Spring boot properties
server.servlet.context-path =/my-context

And this server instance configuration is in my ApplicationConfiguration:

@SpringBootApplication(scanBasePackages = { 
    "t.i.DAO", 
    "t.i.SERVICES",
    "t.i.config", 
    "t.i.config.security" })
@PropertySource({ "classpath:application.mssql.properties" })
@EnableCaching
@EnableTransactionManagement
public class ApplicationConfiguration {

    // Properties of application.mssql.properties file
    @Autowired
    private Environment env;

    ... some code there...

   @Bean
   public ConfigurableServletWebServerFactory undertowServerInstance() { 
        UndertowServletWebServerFactory customizer = new UndertowServletWebServerFactory();
        customizer.addBuilderCustomizers((builder) -> {
            builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true);
        });

        // EDIT: add: set the context by reading app.properties
        customizer.setContextPath(env.getProperty("server.servlet.context-path"));
        return customizer;
   }
}

Notice that with my old spring 1.5 configuration, the method undertowServerInstance() was different:

@Bean
public EmbeddedServletContainerCustomizer undertowServerInstance() {
    return (container) -> {
        if(container instanceof UndertowEmbeddedServletContainerFactory) {
            ((UndertowEmbeddedServletContainerFactory) container)
                .addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true));
        }
    };
}

I use a Spring security configuration class SecurityConfig like this:

package t.i.config;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // allows AOP @PreAuthorize and some other annotations to be applied to methods.
@EnableWebSecurity
@EnableScheduling // allows to run Spring schedulers and periodically run some tasks. We use scheduler for evicting EhCache tokens.
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        ... some code for security configuration ...

        // adding authentication filter
        http.addFilterBefore(new AuthenticationFilter(this.authenticationManager), BasicAuthenticationFilter.class);

    }
}

This class is used to generate a new token from user credentials or check token validity if it is present on header (this filter is called for every requests and it still work):

package t.i.config.security;

public class AuthenticationFilter extends GenericFilterBean {

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

        try {
            // check or create token. Exception if credentials or token invalid
        }
        catch(AuthenticationException authenticationException){
            ((HttpServletResponse) http).sendError(HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage());
            return;
        }

        chain.doFilter(request, response);
    }
}

This is my WebMvcConfig which contain @RestController classes package location (edit: added implements WebMvcConfigurer) :

package t.i.config;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"t.i.controllers"})
public class WebMvcConfig implements WebMvcConfigurer {
 ... code there ... 
}

And, for example, when I call /api/authenticate URL, my spring authentication filter method is called and token created but this WS is never called (returning 405 instead):

package t.i.controllers;

@RestController
public class AuthenticationController {


    @PostMapping("api/authenticate")
    public UserDTO authenticate(AuthenticationWithToken principal) {

        ... some code there that is never called with spring boot 2.0 ...

        return userDTO;
    }
}

I still don't understand this problem and I think something is missing in Undertow configuration.

EDIT: In the ApplicationConfiguration.undertowServerInstance() method, if I remove the line:

// EDIT: add: set the context by reading app.properties
customizer.setContextPath(env.getProperty("server.servlet.context-path"));

Then the AuthenticationFilter is not triggered any more. With spring boot 1.5 I never need to explicitly specify Undertow context.

EDIT: I changed my authentication WS method to GET:

 package t.i.CONTROLLERS;

@RestController
public class AuthenticationController {

    @RequestMapping(value = "/api/authenticate", method = RequestMethod.GET)
    public UserDTO authenticate(AuthenticationWithToken principal) {

        ... some code there that is still never called with spring boot 2.0 ...

        return userDTO;
    }
}

The authentication filter works well but method AuthenticationController.authenticate is still not called. This is that I got with chrome debugger:

In summary:

  • I have 404 error with get request and 405 with post
  • Undertow need the context to be set explicitly otherwise my Authentication filter is not called
It's like spring does not handle any of my @RestController but they are still instanciated (I tested them with console print into constructor).

回答1:

You have your method mapped to a POST request and 405 means method not allowed. That is because your are sending a GET request I assume.

Either change your mapping to @GetMapping("api/authenticate") or use a tool like Postman to send a POST request.

I would recommend the Postman approach, because it is a good idea to do things like auth wuth POST requests.



回答2:

Spring Boot is taking your use of WebMvcConfigurationSupport as an indication that it shouldn't auto-configure Spring MVC. Try replacing extends WebMvcConfigurationSupport with implements WebMvcConfigurer instead.