Add auto-configuration for H2’s web console
Three conditions must be met for the console to be enabled: - H2 is on the classpath - The application is a web application - spring.h2.console.enabled is set to true If spring-boot-devtools is on the classpath, spring.h2.console.enabled will be set to true automatically. Without the dev tools, the enabled property will have to be set to true in application.properties. By default, the console is available at /h2-console. This can be configured via the spring.h2.console.path property. The value of this property must begin with a '/'. When Spring Security is on the classpath the console will be secured based on the user's security.* configuration. When the console is secured, CSRF protection is disabled and frame options is set to SAMEORIGIN for its path. Both settings are required in order for the console to function. Closes gh-766
This commit is contained in:
parent
f95c95a1f1
commit
2a5a32b603
|
|
@ -70,6 +70,11 @@
|
|||
<artifactId>hazelcast-spring</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.samskivert</groupId>
|
||||
<artifactId>jmustache</artifactId>
|
||||
|
|
@ -549,8 +554,9 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
|
|
@ -558,9 +564,13 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.h2;
|
||||
|
||||
import org.h2.server.web.WebServlet;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityAuthorizeMode;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.context.embedded.ServletRegistrationBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for H2's web console
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnClass(WebServlet.class)
|
||||
@ConditionalOnProperty(prefix = "spring.h2.console", name = "enabled", havingValue = "true", matchIfMissing = false)
|
||||
@EnableConfigurationProperties(H2ConsoleProperties.class)
|
||||
@AutoConfigureAfter(SecurityAutoConfiguration.class)
|
||||
public class H2ConsoleAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private H2ConsoleProperties properties;
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean h2Console() {
|
||||
return new ServletRegistrationBean(new WebServlet(), this.properties.getPath()
|
||||
.endsWith("/") ? this.properties.getPath() + "*"
|
||||
: this.properties.getPath() + "/*");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
|
||||
@ConditionalOnBean(ObjectPostProcessor.class)
|
||||
@ConditionalOnProperty(prefix = "security.basic", name = "enabled", matchIfMissing = true)
|
||||
static class H2ConsoleSecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
public WebSecurityConfigurerAdapter h2ConsoleSecurityConfigurer() {
|
||||
return new H2ConsoleSecurityConfigurer();
|
||||
}
|
||||
|
||||
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
|
||||
private static class H2ConsoleSecurityConfigurer extends
|
||||
WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private H2ConsoleProperties console;
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties security;
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity http) throws Exception {
|
||||
HttpSecurity h2Console = http.antMatcher(this.console.getPath().endsWith(
|
||||
"/") ? this.console.getPath() + "**" : this.console.getPath()
|
||||
+ "/**");
|
||||
h2Console.csrf().disable();
|
||||
h2Console.httpBasic();
|
||||
h2Console.headers().frameOptions().sameOrigin();
|
||||
String[] roles = this.security.getUser().getRole().toArray(new String[0]);
|
||||
SecurityAuthorizeMode mode = this.security.getBasic().getAuthorizeMode();
|
||||
if (mode == null || mode == SecurityAuthorizeMode.ROLE) {
|
||||
http.authorizeRequests().anyRequest().hasAnyRole(roles);
|
||||
}
|
||||
else if (mode == SecurityAuthorizeMode.AUTHENTICATED) {
|
||||
http.authorizeRequests().anyRequest().authenticated();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.h2;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Configuration properties for H2's console
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.h2.console")
|
||||
public class H2ConsoleProperties {
|
||||
|
||||
/**
|
||||
* Path at which the console will be available.
|
||||
*/
|
||||
@NotNull
|
||||
@Pattern(regexp = "/[^?#]*", message = "Path must start with /")
|
||||
private String path = "/h2-console";
|
||||
|
||||
/**
|
||||
* Enable the console.
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public boolean getEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfigurat
|
|||
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.h2;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfigurationIntegrationTests.TestConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link H2ConsoleAutoConfiguration}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration(classes = TestConfiguration.class)
|
||||
@TestPropertySource(properties = "spring.h2.console.enabled:true")
|
||||
public class H2ConsoleAutoConfigurationIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void noPrincipal() throws Exception {
|
||||
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
.apply(springSecurity()).build();
|
||||
mockMvc.perform(get("/h2-console/")).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userPrincipal() throws Exception {
|
||||
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
.apply(springSecurity()).build();
|
||||
mockMvc.perform(get("/h2-console/").with(user("test").roles("USER")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string("X-Frame-Options", "SAMEORIGIN"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void someOtherPrincipal() throws Exception {
|
||||
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
.apply(springSecurity()).build();
|
||||
mockMvc.perform(get("/h2-console/").with(user("test").roles("FOO"))).andExpect(
|
||||
status().isForbidden());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ SecurityAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
|
||||
H2ConsoleAutoConfiguration.class })
|
||||
@Controller
|
||||
static class TestConfiguration {
|
||||
|
||||
@RequestMapping("/h2-console/**")
|
||||
public void mockConsole() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.h2;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.context.embedded.ServletRegistrationBean;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link H2ConsoleAutoConfiguration}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class H2ConsoleAutoConfigurationTests {
|
||||
|
||||
private AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setupContext() {
|
||||
this.context.setServletContext(new MockServletContext());
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consoleIsDisabledByDefault() {
|
||||
this.context.register(H2ConsoleAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertThat(this.context.getBeansOfType(ServletRegistrationBean.class).size(),
|
||||
is(equalTo(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertyCanEnableConsole() {
|
||||
this.context.register(H2ConsoleAutoConfiguration.class);
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.h2.console.enabled:true");
|
||||
this.context.refresh();
|
||||
assertThat(this.context.getBeansOfType(ServletRegistrationBean.class).size(),
|
||||
is(equalTo(1)));
|
||||
assertThat(this.context.getBean(ServletRegistrationBean.class).getUrlMappings(),
|
||||
hasItems("/h2-console/*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customPathMustBeginWithASlash() {
|
||||
this.thrown.expect(BeanCreationException.class);
|
||||
this.thrown.expectMessage("Path must start with /");
|
||||
this.context.register(H2ConsoleAutoConfiguration.class);
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.h2.console.enabled:true", "spring.h2.console.path:custom");
|
||||
this.context.refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customPathWithTrailingSlash() {
|
||||
this.context.register(H2ConsoleAutoConfiguration.class);
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.h2.console.enabled:true", "spring.h2.console.path:/custom/");
|
||||
this.context.refresh();
|
||||
assertThat(this.context.getBeansOfType(ServletRegistrationBean.class).size(),
|
||||
is(equalTo(1)));
|
||||
assertThat(this.context.getBean(ServletRegistrationBean.class).getUrlMappings(),
|
||||
hasItems("/custom/*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customPath() {
|
||||
this.context.register(H2ConsoleAutoConfiguration.class);
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.h2.console.enabled:true", "spring.h2.console.path:/custom");
|
||||
this.context.refresh();
|
||||
assertThat(this.context.getBeansOfType(ServletRegistrationBean.class).size(),
|
||||
is(equalTo(1)));
|
||||
assertThat(this.context.getBean(ServletRegistrationBean.class).getUrlMappings(),
|
||||
hasItems("/custom/*"));
|
||||
}
|
||||
}
|
||||
|
|
@ -47,6 +47,7 @@ class DevToolsPropertyDefaultsPostProcessor implements BeanFactoryPostProcessor,
|
|||
properties.put("spring.velocity.cache", "false");
|
||||
properties.put("spring.mustache.cache", "false");
|
||||
properties.put("server.session.persistent", "true");
|
||||
properties.put("spring.h2.console.enabled", "true");
|
||||
PROPERTIES = Collections.unmodifiableMap(properties);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,6 +162,10 @@ content into your application; rather pick only the properties that you need.
|
|||
multipart.max-file-size=1Mb # Max file size.
|
||||
multipart.max-request-size=10Mb # Max request size.
|
||||
|
||||
# H2 Web Console ({sc-spring-boot-autoconfigure}/h2/H2ConsoleProperties.{sc-ext}[H2ConsoleProperties])
|
||||
spring.h2.console.enable=false # Enable the console
|
||||
spring.h2.console.path=/h2-console # Path at which the console can be accessed
|
||||
|
||||
# SPRING HATEOAS ({sc-spring-boot-autoconfigure}/hateoas/HateoasProperties.{sc-ext}[HateoasProperties])
|
||||
spring.hateoas.apply-to-primary-object-mapper=true # if the primary mapper should also be configured
|
||||
|
||||
|
|
|
|||
|
|
@ -2258,6 +2258,45 @@ Hibernate autoconfig is active because the `ddl-auto` settings are more fine-gra
|
|||
|
||||
|
||||
|
||||
[[boot-features-sql-h2-console]]
|
||||
=== Using H2's web console
|
||||
The http://www.h2database.com[H2 database] provides a
|
||||
http://www.h2database.com/html/quickstart.html#h2_console[browser-based console] that
|
||||
Spring Boot can auto-configure for you. The console will be auto-configured when the
|
||||
following conditions are met:
|
||||
|
||||
* You are developing a web application
|
||||
* `com.h2database:h2` is on the classpath
|
||||
* You are using <<using-spring-boot.adoc#using-boot-devtools,Spring Boot's developer
|
||||
tools>>
|
||||
|
||||
TIP: If you are not using Spring Boot's developer tools, but would still like to make use
|
||||
of H2's console, then you can do so by configuring the `spring.h2.console.enabled`
|
||||
property with a value of `true`. The H2 console is only intended for use during
|
||||
development so care should be taken to ensure that `spring.h2.console.enabled` is not set
|
||||
to `true` in production.
|
||||
|
||||
|
||||
|
||||
[[boot-features-sql-h2-console-custom-path]]
|
||||
==== Changing the H2 console's path
|
||||
By default the console will be available at `/h2-console`. You can customize the console's
|
||||
path using the `spring.h2.console.path` property.
|
||||
|
||||
|
||||
|
||||
[[boot-features-sql-h2-console-securing]]
|
||||
==== Securing the H2 console
|
||||
When Spring Security is one the and basic auth is enabled, the H2 console will be
|
||||
automatically secured using basic auth. The following properties can be used to customize
|
||||
the security configuration:
|
||||
|
||||
* `security.user.role`
|
||||
* `security.basic.authorize-mode`
|
||||
* `security.basic.enabled`
|
||||
|
||||
|
||||
|
||||
[[boot-features-jooq]]
|
||||
== Using jOOQ
|
||||
Java Object Oriented Querying (http://www.jooq.org/[jOOQ]) is a popular product from
|
||||
|
|
|
|||
Loading…
Reference in New Issue