diff --git a/docs/src/docs/asciidoc/guides/httpsession-jdbc-boot.adoc b/docs/src/docs/asciidoc/guides/httpsession-jdbc-boot.adoc new file mode 100644 index 00000000..4fb3e80e --- /dev/null +++ b/docs/src/docs/asciidoc/guides/httpsession-jdbc-boot.adoc @@ -0,0 +1,152 @@ += Spring Session - Spring Boot +Rob Winch, Vedran Pavić +:toc: + +This guide describes how to use Spring Session to transparently leverage a relational database to back a web application's `HttpSession` when using Spring Boot. + +NOTE: The completed guide can be found in the <>. + +== Updating Dependencies +Before you use Spring Session, you must ensure to update your dependencies. +We assume you are working with a working Spring Boot web application. +If you are using Maven, ensure to add the following dependencies: + +.pom.xml +[source,xml] +[subs="verbatim,attributes"] +---- + + + + + org.springframework.session + spring-session-jdbc + {spring-session-version} + + + org.springframework.boot + spring-boot-starter-jdbc + + +---- + +ifeval::["{version-snapshot}" == "true"] +Since we are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository. +Ensure you have the following in your pom.xml: + +.pom.xml +[source,xml] +---- + + + + + + spring-snapshot + https://repo.spring.io/libs-snapshot + + +---- +endif::[] + +ifeval::["{version-milestone}" == "true"] +Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository. +Ensure you have the following in your pom.xml: + +.pom.xml +[source,xml] +---- + + spring-milestone + https://repo.spring.io/libs-milestone + +---- +endif::[] + +// tag::config[] + +[[httpsession-jdbc-boot-spring-configuration]] +== Spring Configuration + +After adding the required dependencies, we can create our Spring configuration. +The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session. +Add the following Spring Configuration: + +[source,java] +---- +include::{samples-dir}httpsession-jdbc-boot/src/main/java/sample/config/HttpSessionConfig.java[tags=class] +---- + +<1> The `@EnableJdbcHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter. +The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session. +In this instance Spring Session is backed by a relational database. + +[[httpsession-jdbc-boot-configuration]] +== Configuring the DataSource + +Spring Boot automatically creates a `DataSource` that connects Spring Session to an embedded instance of H2 database. +In a production environment you need to ensure to update your configuration to point to your relational database. +For example, you can include the following in your *application.properties* + +.src/main/resources/application.properties +---- +spring.datasource.url=jdbc:postgresql://localhost:5432/myapp +spring.datasource.username=myapp +spring.datasource.password=secret +---- + +For more information, refer to http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-configure-datasource[Configure a DataSource] portion of the Spring Boot documentation. + +[[httpsession-jdbc-boot-servlet-configuration]] +== Servlet Container Initialization + +Our <> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`. +The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session. + +In order for our `Filter` to do its magic, Spring needs to load our `Config` class. +Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request. +Fortunately, Spring Boot takes care of both of these steps for us. + +// end::config[] + +[[httpsession-jdbc-boot-sample]] +== httpsession-jdbc-boot Sample Application + +The httpsession-jdbc-boot Sample Application demonstrates how to use Spring Session to transparently leverage H2 database to back a web application's `HttpSession` when using Spring Boot. + +[[httpsession-jdbc-boot-running]] +=== Running the httpsession-jdbc-boot Sample Application + +You can run the sample by obtaining the {download-url}[source code] and invoking the following command: + +---- +$ ./gradlew :samples:httpsession-jdbc-boot:bootRun +---- + +You should now be able to access the application at http://localhost:8080/ + +[[httpsession-jdbc-boot-explore]] +=== Exploring the security Sample Application + +Try using the application. Enter the following to log in: + +* **Username** _user_ +* **Password** _password_ + +Now click the **Login** button. +You should now see a message indicating your are logged in with the user entered previously. +The user's information is stored in H2 database rather than Tomcat's `HttpSession` implementation. + +[[httpsession-jdbc-boot-how]] +=== How does it work? + +Instead of using Tomcat's `HttpSession`, we are actually persisting the values in H2 database. +Spring Session replaces the `HttpSession` with an implementation that is backed by a relational database. +When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into H2 database. + +When a new `HttpSession` is created, Spring Session creates a cookie named SESSION in your browser that contains the id of your session. +Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]). + +If you like, you can easily remove the session using H2 web console available at: http://localhost:8080/h2-console/ (use `jdbc:h2:mem:testdb` for JDBC URL) + +Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated. diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc index 8f39c5cb..66376173 100644 --- a/docs/src/docs/asciidoc/index.adoc +++ b/docs/src/docs/asciidoc/index.adoc @@ -119,6 +119,10 @@ If you are looking to get started with Spring Session, the best place to start i | Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store using XML based configuration. | link:guides/httpsession-jdbc-xml.html[HttpSession JDBC XML Guide] +| {gh-samples-url}httpsession-jdbc-boot[HttpSession JDBC Spring Boot] +| Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store when using Spring Boot. +| link:guides/httpsession-jdbc-boot.html[HttpSession JDBC Spring Boot Guide] + |=== [[httpsession]] @@ -277,6 +281,7 @@ You can choose from enabling this using either: * <> * <> +* <> [[httpsession-jdbc-jc]] ==== JDBC Java Based Configuration @@ -298,6 +303,16 @@ You can read the basic steps for integration below, but you are encouraged to fo include::guides/httpsession-jdbc-xml.adoc[tags=config,leveloffset=+3] +[[httpsession-jdbc-boot]] +==== JDBC Spring Boot Based Configuration + +This section describes how to use a relational database to back `HttpSession` when using Spring Boot. + +NOTE: The <> provides a working sample on how to integrate Spring Session and `HttpSession` using Spring Boot. +You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession JDBC Spring Boot Guide when integrating with your own application. + +include::guides/httpsession-jdbc-boot.adoc[tags=config,leveloffset=+3] + [[httpsession-mongo]] === HttpSession with Mongo diff --git a/gradle.properties b/gradle.properties index 10ed8660..63c6dda8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,3 +22,4 @@ spockVersion=1.0-groovy-2.4 jstlVersion=1.2.1 groovyVersion=2.4.4 h2Version=1.4.191 +bootstrapVersion=2.2.2 diff --git a/samples/httpsession-jdbc-boot/README.adoc b/samples/httpsession-jdbc-boot/README.adoc new file mode 100644 index 00000000..d7203b57 --- /dev/null +++ b/samples/httpsession-jdbc-boot/README.adoc @@ -0,0 +1 @@ +Demonstrates using Spring Session with Spring Boot and Spring Security. You can log in with the username "user" and the password "password". \ No newline at end of file diff --git a/samples/httpsession-jdbc-boot/build.gradle b/samples/httpsession-jdbc-boot/build.gradle new file mode 100644 index 00000000..34c71550 --- /dev/null +++ b/samples/httpsession-jdbc-boot/build.gradle @@ -0,0 +1,55 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion") + } +} + +apply plugin: 'spring-boot' + +apply from: JAVA_GRADLE + +tasks.findByPath("artifactoryPublish")?.enabled = false + +group = 'samples' + +dependencies { + compile project(':spring-session-jdbc'), + "org.springframework.boot:spring-boot-starter-jdbc", + "org.springframework.boot:spring-boot-starter-web", + "org.springframework.boot:spring-boot-starter-thymeleaf", + "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect", + "org.webjars:bootstrap:$bootstrapVersion", + "com.h2database:h2", + "org.springframework.security:spring-security-web:$springSecurityVersion", + "org.springframework.security:spring-security-config:$springSecurityVersion" + + testCompile "org.springframework.boot:spring-boot-starter-test" + + integrationTestCompile gebDependencies, + "org.spockframework:spock-spring:$spockVersion" + +} + +integrationTest { + doFirst { + def port = reservePort() + + def host = 'localhost:' + port + systemProperties['geb.build.baseUrl'] = 'http://'+host+'/' + systemProperties['geb.build.reportsDir'] = 'build/geb-reports' + systemProperties['server.port'] = port + systemProperties['management.port'] = 0 + + systemProperties['spring.session.redis.namespace'] = project.name + } +} + +def reservePort() { + def socket = new ServerSocket(0) + def result = socket.localPort + socket.close() + result +} diff --git a/samples/httpsession-jdbc-boot/src/integration-test/groovy/sample/BootTests.groovy b/samples/httpsession-jdbc-boot/src/integration-test/groovy/sample/BootTests.groovy new file mode 100644 index 00000000..f158edbd --- /dev/null +++ b/samples/httpsession-jdbc-boot/src/integration-test/groovy/sample/BootTests.groovy @@ -0,0 +1,75 @@ +/* + * Copyright 2014-2016 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 sample + +import geb.spock.* +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.test.IntegrationTest +import org.springframework.boot.test.SpringApplicationConfiguration +import org.springframework.boot.test.SpringApplicationContextLoader +import org.springframework.test.context.ContextConfiguration +import org.springframework.test.context.web.WebAppConfiguration +import sample.pages.HomePage +import sample.pages.LoginPage +import spock.lang.Stepwise +import pages.* + +/** + * Tests the demo that supports multiple sessions + * + * @author Rob Winch + */ +@Stepwise +@ContextConfiguration(classes = Application, loader = SpringApplicationContextLoader) +@WebAppConfiguration +@IntegrationTest +class BootTests extends GebReportingSpec { + + def 'Unauthenticated user sent to log in page'() { + when: 'unauthenticated user request protected page' + via HomePage + then: 'sent to the log in page' + at LoginPage + } + + def 'Log in views home page'() { + when: 'log in successfully' + login() + then: 'sent to original page' + at HomePage + and: 'the username is displayed' + username == 'user' + and: 'Spring Session Management is being used' + driver.manage().cookies.find { it.name == 'SESSION' } + and: 'Standard Session is NOT being used' + !driver.manage().cookies.find { it.name == 'JSESSIONID' } + } + + def 'Log out success'() { + when: + logout() + then: + at LoginPage + } + + def 'Logged out user sent to log in page'() { + when: 'logged out user request protected page' + via HomePage + then: 'sent to the log in page' + at LoginPage + } +} diff --git a/samples/httpsession-jdbc-boot/src/integration-test/groovy/sample/pages/HomePage.groovy b/samples/httpsession-jdbc-boot/src/integration-test/groovy/sample/pages/HomePage.groovy new file mode 100644 index 00000000..ae2c3e94 --- /dev/null +++ b/samples/httpsession-jdbc-boot/src/integration-test/groovy/sample/pages/HomePage.groovy @@ -0,0 +1,33 @@ +/* + * Copyright 2014-2016 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 sample.pages + +import geb.* + +/** + * The home page + * + * @author Rob Winch + */ +class HomePage extends Page { + static url = '' + static at = { assert driver.title == 'Spring Session Sample - Secured Content'; true} + static content = { + username { $('#un').text() } + logout(to:LoginPage) { $('input[type=submit]').click() } + } +} diff --git a/samples/httpsession-jdbc-boot/src/integration-test/groovy/sample/pages/LoginPage.groovy b/samples/httpsession-jdbc-boot/src/integration-test/groovy/sample/pages/LoginPage.groovy new file mode 100644 index 00000000..68f98597 --- /dev/null +++ b/samples/httpsession-jdbc-boot/src/integration-test/groovy/sample/pages/LoginPage.groovy @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2016 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 sample.pages + +import geb.* + +/** + * The Links Page + * + * @author Rob Winch + */ +class LoginPage extends Page { + static url = '/login' + static at = { assert driver.title == 'Login Page'; true} + static content = { + form { $('form') } + submit { $('input[type=submit]') } + login(required:false) { user='user', pass='password' -> + form.username = user + form.password = pass + submit.click(HomePage) + } + } +} diff --git a/samples/httpsession-jdbc-boot/src/main/java/sample/Application.java b/samples/httpsession-jdbc-boot/src/main/java/sample/Application.java new file mode 100644 index 00000000..02627e78 --- /dev/null +++ b/samples/httpsession-jdbc-boot/src/main/java/sample/Application.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014-2016 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 sample; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author Rob Winch + */ +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/samples/httpsession-jdbc-boot/src/main/java/sample/config/HttpSessionConfig.java b/samples/httpsession-jdbc-boot/src/main/java/sample/config/HttpSessionConfig.java new file mode 100644 index 00000000..946ded9c --- /dev/null +++ b/samples/httpsession-jdbc-boot/src/main/java/sample/config/HttpSessionConfig.java @@ -0,0 +1,25 @@ +/* + * Copyright 2014-2016 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 sample.config; + +import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession; + +// tag::class[] +@EnableJdbcHttpSession // <1> +public class HttpSessionConfig { +} +// end::class[] diff --git a/samples/httpsession-jdbc-boot/src/main/java/sample/config/SecurityConfig.java b/samples/httpsession-jdbc-boot/src/main/java/sample/config/SecurityConfig.java new file mode 100644 index 00000000..602ac1a8 --- /dev/null +++ b/samples/httpsession-jdbc-boot/src/main/java/sample/config/SecurityConfig.java @@ -0,0 +1,51 @@ +/* + * Copyright 2014-2016 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 sample.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +/** + * @author Rob Winch + * @author Vedran Pavic + */ +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + // @formatter:off + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user") + .password("password") + .roles("USER"); + } + // @formatter:on + + // @formatter:off + @Override + public void configure(WebSecurity web) throws Exception { + web + .ignoring().antMatchers("/h2-console/**"); + } + // @formatter:on + +} diff --git a/samples/httpsession-jdbc-boot/src/main/java/sample/mvc/IndexController.java b/samples/httpsession-jdbc-boot/src/main/java/sample/mvc/IndexController.java new file mode 100644 index 00000000..8c94fb38 --- /dev/null +++ b/samples/httpsession-jdbc-boot/src/main/java/sample/mvc/IndexController.java @@ -0,0 +1,34 @@ +/* + * Copyright 2014-2016 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 sample.mvc; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * Controller for sending the user to the login view. + * + * @author Rob Winch + * + */ +@Controller +public class IndexController { + @RequestMapping("/") + public String index() { + return "index"; + } +} diff --git a/samples/httpsession-jdbc-boot/src/main/resources/application.properties b/samples/httpsession-jdbc-boot/src/main/resources/application.properties new file mode 100644 index 00000000..7e0549f7 --- /dev/null +++ b/samples/httpsession-jdbc-boot/src/main/resources/application.properties @@ -0,0 +1,4 @@ +spring.thymeleaf.cache=false +spring.template.cache=false +spring.datasource.schema=classpath:org/springframework/session/jdbc/schema-h2.sql +spring.h2.console.enabled=true diff --git a/samples/httpsession-jdbc-boot/src/main/resources/static/resources/img/favicon.ico b/samples/httpsession-jdbc-boot/src/main/resources/static/resources/img/favicon.ico new file mode 100644 index 00000000..bfb99740 Binary files /dev/null and b/samples/httpsession-jdbc-boot/src/main/resources/static/resources/img/favicon.ico differ diff --git a/samples/httpsession-jdbc-boot/src/main/resources/static/resources/img/logo.png b/samples/httpsession-jdbc-boot/src/main/resources/static/resources/img/logo.png new file mode 100644 index 00000000..39323088 Binary files /dev/null and b/samples/httpsession-jdbc-boot/src/main/resources/static/resources/img/logo.png differ diff --git a/samples/httpsession-jdbc-boot/src/main/resources/templates/index.html b/samples/httpsession-jdbc-boot/src/main/resources/templates/index.html new file mode 100644 index 00000000..1e2703eb --- /dev/null +++ b/samples/httpsession-jdbc-boot/src/main/resources/templates/index.html @@ -0,0 +1,11 @@ + + + Secured Content + + +
+

Secured Page

+

This page is secured using Spring Boot, Spring Session, and Spring Security.

+
+ + diff --git a/samples/httpsession-jdbc-boot/src/main/resources/templates/layout.html b/samples/httpsession-jdbc-boot/src/main/resources/templates/layout.html new file mode 100644 index 00000000..5bb60fa3 --- /dev/null +++ b/samples/httpsession-jdbc-boot/src/main/resources/templates/layout.html @@ -0,0 +1,122 @@ + + + + Spring Session Sample + + + + + + + + + + + +
+ + +
+
+ Some Success message +
+
+ Fake content +
+
+ +
+
+ + + + diff --git a/settings.gradle b/settings.gradle index da43a162..5c190e7b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,6 +13,7 @@ include 'samples:httpsession-gemfire-clientserver-xml' include 'samples:httpsession-gemfire-p2p' include 'samples:httpsession-gemfire-p2p-xml' include 'samples:httpsession-jdbc' +include 'samples:httpsession-jdbc-boot' include 'samples:httpsession-jdbc-xml' include 'samples:httpsession-xml' include 'samples:rest'