Tuhin Kanti Sharma
Tuhin Kanti Sharma

Reputation: 1126

Spring Boot /h2-console throws 403 with Spring Security 1.5.2

We recently upgraded from Spring Boot 1.4.1 to 1.5.2. One of the features of 1.5.2 is that if Spring Security is part of the package then it is protected by basic auth. I am unable to access the /h2-console even after basic auth. It throws 403 forbidden.

application.yml:

spring:
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:file:../app-db/app_db;AUTO_SERVER=TRUE
    username: sa
    password: sa
    initialize: false
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: true
    database-platform: org.hibernate.dialect.H2Dialect
  h2:
    console:
      enabled: true
      settings:
        web-allow-others: true
  allowed:
    resources: /h2-console/**

I have even explicitly allowed /h2-console/**

 httpSecurity.authorizeRequests()
                .antMatchers(allowedResources)                  
                .permitAll()

I keep getting 403 when trying to access localhost:8080/h2-console. I tried many settings as well as putting:

management.security.enabled=true
security.basic.enabled=true

But I am unable to access the h2-console.

Upvotes: 50

Views: 68699

Answers (18)

Park JongBum
Park JongBum

Reputation: 1403

As of 2024 July, with up-to-date S/W versions, this Bean solved the h2-console access 403 error:

@Bean
WebSecurityCustomizer ignoringCustomizer() {
  return (web) -> web.ignoring().requestMatchers("/h2-console/**");
}

Refer https://www.baeldung.com/spring-security-httpsecurity-vs-websecurity#1-the-ignoring-method

For your reference, included is a screen-shot containing the above bean - enter image description here

Upvotes: 1

Abd Abughazaleh
Abd Abughazaleh

Reputation: 5535

For Spring Security +6.2 and 7

@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((requests) -> requests
            .requestMatchers("/test/**").authenticated()
            .requestMatchers("/h2-console/**").permitAll());
    http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll());
    http.formLogin(Customizer.withDefaults());
    http.httpBasic(Customizer.withDefaults());
    http.csrf(AbstractHttpConfigurer::disable);
    http.headers(h -> h.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));
    return (SecurityFilterChain) http.build();
}

Upvotes: 1

Mayuresh
Mayuresh

Reputation: 1

the only reason mine wasn't working because i had not used the @Bean annotation and hence the springsecurity didn't new about my method

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(auth->auth.anyRequest().permitAll());
    
    //in case of direct page visit show the login page
    http.formLogin(withDefaults());
    
    http.csrf(csrf->csrf.disable());
    http.headers(headers->headers.frameOptions(frameoptions->frameoptions.disable()));
    
    return http.build();
}

Upvotes: 0

Hassen Gaaya
Hassen Gaaya

Reputation: 108

Here is an updated config example for spring security 6

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    MvcRequestMatcher.Builder matcher(HandlerMappingIntrospector handlerMappingIntrospector) {
        return new MvcRequestMatcher.Builder(handlerMappingIntrospector);
    }


    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, MvcRequestMatcher.Builder mvcMatcher) throws Exception {

        http.authorizeHttpRequests((authorize) -> authorize
                        .requestMatchers(mvcMatcher.pattern("/endpoint1")).permitAll() // just an example of an endpoint
                        .requestMatchers(mvcMatcher.servletPath("/h2-console").pattern("**")).permitAll()
                        .anyRequest().authenticated()
                )
                .csrf(AbstractHttpConfigurer::disable)
                .headers(httpSecurityHeadersConfigurer -> httpSecurityHeadersConfigurer.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));
        return http.build();
    }
}

Upvotes: 2

Mohit Sehgal
Mohit Sehgal

Reputation: 332

With Spring Boot 3, the following works for me:

@Configuration 
class securityConfig {
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().requestMatchers(new AntPathRequestMatcher("/h2-console/**"));
    }
}

Upvotes: 14

Khushboo
Khushboo

Reputation: 3163

With SpringBoot version 3.0.3 and Java 17, the below code works for me:

@Configuration
public class SecurityConfiguration {

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().anyRequest();
    }
}

Application.properties file contains below configuration:

spring.application.name=order-service
server.port=8080
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:dbapp
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

spring.security.user.name=user
spring.security.user.password=password

Upvotes: 5

user21104075
user21104075

Reputation: 109

Spring Boot's PathRequest class has a method toH2Console() that returns RequestMatcher. It reads H2 console path from H2ConsoleProperties, so if you have modified spring.h2.console.path property, you'll get a corresponding matcher

public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
            .csrf().ignoringRequestMatchers(PathRequest.toH2Console()).and()
            .headers().frameOptions().sameOrigin().and()
            // ...

You could use same matcher in http.authorizeHttpRequests(...)


Also if you wonder why .csrf().ignoringRequestMatchers("h2-console/**") doesn't work, that's because MvcRequestMatcher treats requests as contextPath + pathWithinApplication and tries to match pattern with the latter, e.g. /h2-console/login.do has contextPath = h2-console and pathWithinApplication = login.do, and "h2-console/**" pattern doesn't match login.do.

So you would have to write something like this

public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector handlerMappingIntrospector) throws Exception {
    MvcRequestMatcher mvcRequestMatcher = new MvcRequestMatcher.Builder(handlerMappingIntrospector)
            .servletPath("/h2-console")
            .pattern("/**");

    return http
            .csrf().ignoringRequestMatchers(mvcRequestMatcher).and()
            .headers().frameOptions().sameOrigin().and()
            // ...

(but still I would recommend using .toH2Console() option)

Upvotes: 0

Torakun
Torakun

Reputation: 73

As WebSecurityConfigurerAdapter is deprecated from Spring Security 5.7.0-M2, a new way to do this is to use permitAll via HttpSecurity#authorizeHttpRequests as recommended in this Spring blog. You also need to disable CSRF but only for H2-console. And then allow X-Frame options for frames from the same origin of the page. It is still not recommended to use this in production but with this, you can continue to test your security with CSRF enable for the rest of the site while still having access to H2-console. Updated for Spring Security 6.

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http.authorizeHttpRequests()
            .requestMatchers(AntPathRequestMatcher.antMatcher("/h2-console/**")).permitAll()
            .and()
            .csrf().ignoringRequestMatchers(AntPathRequestMatcher.antMatcher("/h2-console/**"))
            .and()
            .headers(headers -> headers.frameOptions().sameOrigin())
            .build();
}

Upvotes: 5

Vijay Pant
Vijay Pant

Reputation: 104

Please note these settings are not recommended for Production:

To enable access to the h2 console, which spring security disallows, do this:

  1. Allow request to "/console/*" in your SecurityFilterChain bean.
  2. Disable Cross Site Request Forgery protection.
  3. Disable X-Frame-Options.

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
            .authorizeRequests()
                .antMatchers("/", "/**", "/console/**").permitAll()             
                .and()
                .csrf().disable()
                .headers().frameOptions().disable()             
                .build();
}

Upvotes: 1

Vikas Tawniya
Vikas Tawniya

Reputation: 1451

Though the top voted answer is correct.

As of now WebSecurityConfigurerAdapter is deprecated in newer spring security version and the way to go about it is creating Bean for WebSecurityCustomizer. Below Bean in your security configuration class can do the trick.

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
    return (web) -> web.ignoring().antMatchers("/h2-console/**");
}

Upvotes: 7

Az.MaYo
Az.MaYo

Reputation: 1106

With respect to WebSecurityConfigurerAdapter I think more appropriate and well explained answer is available here Although I have added sample code and it's works fine for me, not only for h2-console but also for Swagger-UI.

private static final String[] AUTH_WHITELIST = {
        // -- Swagger UI v2
        "/v2/api-docs",
        "/swagger-resources",
        "/swagger-resources/**",
        "/configuration/ui",
        "/configuration/security",
        "/swagger-ui.html",
        "/webjars/**",
        // -- Swagger UI v3 (OpenAPI)
        "/v3/api-docs/**",
        "/swagger-ui/**",
        // other public endpoints
        "/h2-console/**",
 };
@Override
protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests().antMatchers("/hello").hasAuthority("USER")
                .and().authorizeRequests().antMatchers(AUTH_WHITELIST).permitAll().anyRequest().authenticated()
                .and().headers().frameOptions().sameOrigin()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);            
}

Upvotes: 2

Senthuran
Senthuran

Reputation: 1837

I also encountered the same problem when I'm using spring security. Please note the below configuration in the application.properties

spring.h2.console.enabled=true
spring.h2.console.path=/h2

spring.datasource.url=jdbc:h2:file:~/test
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver

In the security configuration under the configure method I've included the following and I'm able to access the h2 console.

        .antMatchers( "/h2/**").permitAll()

Upvotes: 2

argoth
argoth

Reputation: 1253

Spring security blocks /h2-console (or the path you configured in your application.yaml) path for H2 database.

To access the H2 console just add the below code to your WebSecurityConfigurerAdapter.

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/h2-console/**").permitAll();

        http.csrf().disable();
        http.headers().frameOptions().disable();
    }
}

Don't use this configuration in a production environment. =)

Upvotes: 91

Parakrama Dharmapala
Parakrama Dharmapala

Reputation: 1229

Since H2 has it's own authentication provider, you can skip the Spring Security for the path of h2 console entirely in the same way that you do for your static content.

In order to do that, in your Spring security config, you have to override the configuration method which takes an instance of org.springframework.security.config.annotation.web.builders.WebSecurity as a parameter instead of the one which takes an instance of org.springframework.security.config.annotation.web.builders.HttpSecurity

    @Override
    public void configure(WebSecurity web) throws Exception {
        web
            .ignoring()
            .antMatchers("/h2-console/**");
    }

If you're using h2 in a production environment, make sure you set up the proper security measures (things like, setting a non-obvious path, good password, ip white list) for your h2 console.

Upvotes: 100

Chao Luo
Chao Luo

Reputation: 2696

@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnProperty(prefix = "security.basic", name = "enabled", matchIfMissing = true)
static class H2ConsoleSecurityConfiguration 

As you can see the source code in spring boot, if you enable the basic, the spring boot will load spring security configuration H2ConsoleSecurityConfigurer with order SecurityProperties.BASIC_AUTH_ORDER - 10, and the authentication is base on your configuration in security. This is the default security configuration:

public void configure(HttpSecurity http) throws Exception {
            String path = this.console.getPath();
            String antPattern = path.endsWith("/")?path + "**":path + "/**";
            HttpSecurity h2Console = http.antMatcher(antPattern);
            h2Console.csrf().disable();
            h2Console.httpBasic();
            h2Console.headers().frameOptions().sameOrigin();
            // the default role is `USER` and `management.security.roles`
            String[] roles = (String[])this.security.getUser().getRole().toArray(new String[0]);
           // this value is base `security.basic.authorize-mode`, `role`, 'authenticated' and `none`
            SecurityAuthorizeMode mode = this.security.getBasic().getAuthorizeMode();
            if(mode != null && mode != SecurityAuthorizeMode.ROLE) {
                if(mode == SecurityAuthorizeMode.AUTHENTICATED) {
                    ((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated();
                }
            } else {
                ((AuthorizedUrl)http.authorizeRequests().anyRequest()).hasAnyRole(roles);
            }

        }

and you can also create a new configuration to override the default one.

@Configuration
// before the default configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 11)
class CustomH2ConsoleSecurityConfigurer extends WebSecurityConfigurerAdapter {

        @Autowired
        private H2ConsoleProperties console;

        @Override
        public void configure(HttpSecurity http) throws Exception {
            String path = this.console.getPath();
            String antPattern = (path.endsWith("/") ? path + "**" : path + "/**");
            HttpSecurity h2Console = http.antMatcher(antPattern);
            h2Console.csrf().disable();
            h2Console.httpBasic();
            h2Console.headers().frameOptions().sameOrigin();
            // config as you like
            http.authorizeRequests().anyRequest().permitAll();
        }

    }

Upvotes: 3

Adolfo Ruiz Ruiz
Adolfo Ruiz Ruiz

Reputation: 13

This also helps for me

  #H2 database
    datasource:
      url: jdbc:h2:mem:mytestdb;INIT=RUNSCRIPT FROM 'classpath:/data.sql'
      driverClassName: org.h2.Driver
      username: sa
      password: sa
    main:
        allow-bean-definition-overriding: true
    h2:
      console:
        enabled: true
        path: /h2-console
        settings:
          web-allow-others: true
    allowed:
      resources: /h2-console/**
    security:
      basic:
        enabled: true
        authorize-mode: NONE

Upvotes: 0

Vadim Kirilchuk
Vadim Kirilchuk

Reputation: 3542

I want to provide configuration similar to what is proposed by @argoth, but a bit more production ready :)

@Profile("h2") // to make sure it is active only if h2 profile is active
@Configuration
@ConditionalOnProperty( //to make sure it is active if console is enabled
    value="spring.h2.console.enabled", 
    havingValue = "true", 
    matchIfMissing = false)
public class H2SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // this may not be required, depends on your app configuration
        http.authorizeRequests()
                // we need config just for console, nothing else             
                .antMatchers("/h2_console/**").permitAll();
        // this will ignore only h2-console csrf, spring security 4+
        http.csrf().ignoringAntMatchers("/h2-console/**");
        //this will allow frames with same origin which is much more safe
        http.headers().frameOptions().sameOrigin();
    }
}

In fact there was similar configuration done in boot 1.3 which was called H2ConsoleSecurityConfiguration, but now it's gone: Old class

github discussion

Upd. very important note here! When you have multiple WebSecurityConfigurerAdapter they may conflict with each other, so if you have another WebSecurityConfigurerAdapter in your code, you will need to somehow merge them. To give you more details on why there will be a conflict, it will happen due to each adapter setting up it's own filter chain, and every request will have to pass both filter chains. If one of the chains forbids frameOptions and other doesn't the request won't pass the first chain.. That said, please, be careful with multiple configurers..

Upvotes: 5

Tuhin Kanti Sharma
Tuhin Kanti Sharma

Reputation: 1126

I enabled debug logging and saw this:

o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /h2-console/; Attributes: [hasAnyRole('ROLE_USER','ROLE_ACTUATOR')]
2017-05-05 13:16:09.304 DEBUG 90365 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@33d2af72: Principal: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl@7371d5f4: Dn: cn=XYZ,ou=XYZ,ou=Active,ou=ABC_USERS,dc=internal,dc=organization,dc=com; Username: uname; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; CredentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 86EF50EF548ED4DBCE4D661AEC93F88C; Granted Authorities: ROLE_ADMIN
2017-05-05 13:16:09.305 DEBUG 90365 --- [nio-8080-exec-2] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@51d3d69, returned: -1
2017-05-05 13:16:09.305 DEBUG 90365 --- [nio-8080-exec-2] o.s.s.w.a.ExceptionTranslationFilter     : Access is denied (user is not anonymous); delegating to AccessDeniedHandler

I realize that my user does not have the ROLE_USER. I was assuming ROLE_ADMIN > ROLE_USER but I still need to understand this a little better.

I updated my settings to:

security:
  basic:
    enabled: true
    authorize-mode: NONE

I am able to access the /h2-console/** now.

Upvotes: 3

Related Questions