diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml
index bedbc8262d..a68effcaf0 100644
--- a/spring-boot-autoconfigure/pom.xml
+++ b/spring-boot-autoconfigure/pom.xml
@@ -70,6 +70,11 @@
hazelcast-spring
true
+
+ com.h2database
+ h2
+ true
+
com.samskivert
jmustache
@@ -549,8 +554,9 @@
test
- org.yaml
- snakeyaml
+ org.slf4j
+ slf4j-jdk14
+ test
org.springframework
@@ -558,9 +564,13 @@
test
- org.slf4j
- slf4j-jdk14
+ org.springframework.security
+ spring-security-test
test
+
+ org.yaml
+ snakeyaml
+
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.java
new file mode 100644
index 0000000000..944c9d2e65
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.java
@@ -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();
+ }
+ }
+
+ }
+
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleProperties.java
new file mode 100644
index 0000000000..498706ad23
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleProperties.java
@@ -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;
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
index efe6b7a887..6fd13ee103 100644
--- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -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,\
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationIntegrationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationIntegrationTests.java
new file mode 100644
index 0000000000..a7299b2d4b
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationIntegrationTests.java
@@ -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() {
+
+ }
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationTests.java
new file mode 100644
index 0000000000..04ad734e90
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationTests.java
@@ -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/*"));
+ }
+}
diff --git a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsPropertyDefaultsPostProcessor.java b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsPropertyDefaultsPostProcessor.java
index 45815b8221..785b65a12c 100644
--- a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsPropertyDefaultsPostProcessor.java
+++ b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsPropertyDefaultsPostProcessor.java
@@ -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);
}
diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
index 64d40d1647..4d28ce6125 100644
--- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
+++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
@@ -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
diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
index 130f20cacc..6bfb832a9b 100644
--- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
+++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
@@ -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 <>
+
+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