spring-bootspring-mvcspring-securityh2web-console

Spring + H2DB-Web-Console: "This method cannot decide whether these patterns are Spring MVC patterns or not."


My ultimate goal is the use the h2db-web-console in my application, as part of my local dev environment.

I'm getting the error:

Caused by: java.lang.IllegalArgumentException: This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).

This is because there is more than one mappable servlet in your servlet context: {org.h2.server.web.JakartaWebServlet=[/my-h2-console/*], org.springframework.web.servlet.DispatcherServlet=[/]}.

For each MvcRequestMatcher, call MvcRequestMatcher#setServletPath to indicate the servlet path.

While this sounds pretty descriptive it drives me crazy, since it appears it doesn't matter which path I use for JakartaWebServlet=[/my-h2-console/*], since DispatcherServlet=[/] simply matches everything. that starts with "/"...which is everything.

...please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher)...

Well, those are deprecated with Spring 3.x.x patchnotes so I've tried using the RequestMatcher(this should be automatically be used with 'authorize') instead.

Security-Config

    @Bean
    @Order(GENERAL_SECURITY_CONFIGURATION_ORDER)
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            ...
            }
            securityMatcher("/**")
            headers { frameOptions { disable() } }
            csrf { ignoringRequestMatchers("/my-h2-console/**") }

            authorizeRequests {
                authorize("/my-h2-console/**", permitAll)
                authorize("/ping", permitAll)
                authorize("/**", denyAll)
            }
        }
        return http.build()
    }

pom.xml

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope> <!-- Should be "test", revert later -->
        </dependency>

application.yml

spring:
  h2:
    console:
      enabled: true
      path: /my-h2-console
      settings:
        trace: false
        web-allow-others: false
  datasource:
    url: jdbc:h2:mem:testdb
    driverClassName: org.h2.Driver
    username: sa
    password:
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
  sql:
    init:
      mode: embedded
      schema-locations: classpath:sql/schema.testdb.local.sql

Please keep in mind, I'm new to spring-boot in general, so please be gentle. Any kind of information regarding this topic is kindly appreciated. :)

I tried:

=> Only turning off/on

spring:
  h2:
    console:
      enabled: true

seems to bring any change.


Solution

  • This configuration helped me to resolve H2 issue:

    @Configuration
    public class SecurityConfig {
    
        // My enpdoints start from /v1 so this pattern is ok for me
        private static final String API_URL_PATTERN = "/v1/**";
    
        @Bean
        public SecurityFilterChain getSecurityFilterChain(HttpSecurity http,
                                                          HandlerMappingIntrospector introspector) throws Exception {
            MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
    
            http.csrf(csrfConfigurer ->
                    csrfConfigurer.ignoringRequestMatchers(mvcMatcherBuilder.pattern(API_URL_PATTERN),
                            PathRequest.toH2Console()));
    
            http.headers(headersConfigurer ->
                    headersConfigurer.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin));
    
            http.authorizeHttpRequests(auth ->
                    auth
                            .requestMatchers(mvcMatcherBuilder.pattern(API_URL_PATTERN)).permitAll()
                            //This line is optional in .authenticated() case as .anyRequest().authenticated()
                            //would be applied for H2 path anyway
                            .requestMatchers(PathRequest.toH2Console()).authenticated()
                            .anyRequest().authenticated()
            );
    
            http.formLogin(Customizer.withDefaults());
            http.httpBasic(Customizer.withDefaults());
    
            return http.build();
        }
    }
    

    More info.

    Updated code for H2 console to work properly in browser.

    Also PathRequest.toH2Console() could be used instead of AntPathRequestMatcher.antMatcher("/h2-console/**")