Compare commits

..

6 Commits

Author SHA1 Message Date
Eleftheria Stein
e859da6d27 Release 2.2.2.RELEASE 2020-03-04 16:03:14 -05:00
Eleftheria Stein
028bae1f11 Upgrade samples to Spring Boot 2.2.5
Resolves #1596
2020-03-04 10:50:54 -05:00
Eleftheria Stein
234cb6dd88 Upgrade Spring Security to 5.2.2.RELEASE
Resolves #1595
2020-03-04 10:50:05 -05:00
Eleftheria Stein
43101308ec Upgrade Spring Data to Moore-SR5
Resolves #1594
2020-03-04 10:49:17 -05:00
Eleftheria Stein
089f6b92de Upgrade Spring Framework to 5.2.4.RELEASE
Resolves #1593
2020-03-04 10:48:41 -05:00
Eleftheria Stein
c6d129a5a5 Upgrade Reactor to Dysprosium-SR5
Resolves #1592
2020-03-04 10:42:02 -05:00
64 changed files with 154 additions and 852 deletions

7
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,7 @@
<!--
For Security Vulnerabilities, please use https://pivotal.io/security#reporting
-->
<!--
Thanks for raising a Spring Session issue. Please provide a brief description of your problem along with the version of Spring Session that you are using. If possible, please also consider putting together a sample application that reproduces the issue.
-->

View File

@@ -1,24 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'type: bug, status: waiting-for-triage'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Sample**
A link to a GitHub repository with a [minimal, reproducible sample](https://stackoverflow.com/help/minimal-reproducible-example).
Reports that include a sample will take priority over reports that do not.
At times, we may require a sample, so it is good to try and include a sample up front.

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Community Support
url: https://stackoverflow.com/questions/tagged/spring-security
about: Please ask and answer questions on StackOverflow with the tag spring-session

View File

@@ -1,25 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'status: waiting-for-triage, type: enhancement'
assignees: ''
---
**Expected Behavior**
<!--- Tell us how it should work -->
**Current Behavior**
<!--- Explain the difference from current behavior -->
**Context**
<!---
How has this issue affected you?
What are you trying to accomplish?
What other alternatives have you considered?
Are you aware of any workarounds?
-->

1
.gitignore vendored
View File

@@ -13,4 +13,3 @@ out
.checkstyle
!etc/eclipse/.checkstyle
!**/src/**/build
.DS_Store

44
CODE_OF_CONDUCT.adoc Normal file
View File

@@ -0,0 +1,44 @@
= Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open
and welcoming community, we pledge to respect all people who contribute through reporting
issues, posting feature requests, updating documentation, submitting pull requests or
patches, and other activities.
We are committed to making participation in this project a harassment-free experience for
everyone, regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses,
without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or reject comments,
commits, code, wiki edits, issues, and other contributions that are not aligned to this
Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to fairly and
consistently applying these principles to every aspect of managing this project. Project
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
from the project team.
This Code of Conduct applies both within project spaces and in public spaces when an
individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will
be reviewed and investigated and will result in a response that is deemed necessary and
appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
with regard to the reporter of an incident.
This Code of Conduct is adapted from the
https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]

View File

@@ -3,16 +3,9 @@
Spring Session is released under the Apache 2.0 license. If you would like to contribute
something, or simply want to hack on the code this document should help you get started.
== Code of Conduct
Please see our https://github.com/spring-projects/.github/blob/master/CODE_OF_CONDUCT.md[code of conduct]
== Reporting Security Vulnerabilities
Please see our https://github.com/spring-projects/spring-session/security/policy[Security policy].
This project adheres to the Contributor Covenant link:CODE_OF_CONDUCT.adoc[code of conduct].
By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
== Using GitHub issues

View File

@@ -18,16 +18,10 @@ Spring Session consists of the following modules:
* Spring Session JDBC - provides `SessionRepository` implementation backed by a relational database and configuration support
* Spring Session Hazelcast - provides `SessionRepository` implementation backed by Hazelcast and configuration support
== Code of Conduct
Please see our https://github.com/spring-projects/.github/blob/master/CODE_OF_CONDUCT.md[code of conduct]
== Reporting Security Vulnerabilities
Please see our https://github.com/spring-projects/spring-session/security/policy[Security policy].
This project adheres to the Contributor Covenant link:CODE_OF_CONDUCT.adoc[code of conduct].
By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
== Spring Session Project Site

View File

@@ -4,7 +4,7 @@ buildscript {
snapshotBuild = version.endsWith('SNAPSHOT')
milestoneBuild = !(releaseBuild || snapshotBuild)
springBootVersion = '2.3.1.RELEASE'
springBootVersion = '2.2.5.RELEASE'
}
repositories {
@@ -13,7 +13,7 @@ buildscript {
}
dependencies {
classpath 'io.spring.gradle:spring-build-conventions:0.0.32.RELEASE'
classpath 'io.spring.gradle:spring-build-conventions:0.0.27.RELEASE'
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
}
}

View File

@@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.parallel=true
version=2.4.0-M1
version=2.2.2.RELEASE

View File

@@ -1,35 +1,35 @@
dependencyManagement {
imports {
mavenBom 'io.projectreactor:reactor-bom:Dysprosium-SR9'
mavenBom 'org.junit:junit-bom:5.6.2'
mavenBom 'org.springframework:spring-framework-bom:5.3.0-M1'
mavenBom 'org.springframework.data:spring-data-bom:2020.0.0-M1'
mavenBom 'org.springframework.security:spring-security-bom:5.3.3.RELEASE'
mavenBom 'org.testcontainers:testcontainers-bom:1.14.3'
mavenBom 'io.projectreactor:reactor-bom:Dysprosium-SR5'
mavenBom 'org.junit:junit-bom:5.5.2'
mavenBom 'org.springframework:spring-framework-bom:5.2.4.RELEASE'
mavenBom 'org.springframework.data:spring-data-releasetrain:Moore-SR5'
mavenBom 'org.springframework.security:spring-security-bom:5.2.2.RELEASE'
mavenBom 'org.testcontainers:testcontainers-bom:1.12.2'
}
dependencies {
dependencySet(group: 'com.hazelcast', version: '3.12.7') {
dependencySet(group: 'com.hazelcast', version: '3.12.5') {
entry 'hazelcast'
entry 'hazelcast-client'
}
dependency 'com.h2database:h2:1.4.200'
dependency 'com.h2database:h2:1.4.199'
dependency 'com.ibm.db2:jcc:11.5.0.0'
dependency 'com.microsoft.sqlserver:mssql-jdbc:7.4.1.jre8'
dependency 'com.oracle.database.jdbc:ojdbc8:19.6.0.0'
dependency 'com.zaxxer:HikariCP:3.4.5'
dependency 'com.oracle.ojdbc:ojdbc8:19.3.0.0'
dependency 'com.zaxxer:HikariCP:3.4.1'
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
dependency 'io.lettuce:lettuce-core:5.3.1.RELEASE'
dependency 'io.lettuce:lettuce-core:5.2.0.RELEASE'
dependency 'javax.annotation:javax.annotation-api:1.3.2'
dependency 'javax.servlet:javax.servlet-api:4.0.1'
dependency 'junit:junit:4.13'
dependency 'mysql:mysql-connector-java:8.0.20'
dependency 'junit:junit:4.12'
dependency 'mysql:mysql-connector-java:8.0.17'
dependency 'org.apache.derby:derby:10.14.2.0'
dependency 'org.assertj:assertj-core:3.16.1'
dependency 'org.assertj:assertj-core:3.13.2'
dependency 'org.hsqldb:hsqldb:2.5.0'
dependency 'org.mariadb.jdbc:mariadb-java-client:2.6.1'
dependency 'org.mockito:mockito-core:3.3.3'
dependency 'org.postgresql:postgresql:42.2.14'
dependency 'org.mariadb.jdbc:mariadb-java-client:2.4.4'
dependency 'org.mockito:mockito-core:3.0.0'
dependency 'org.postgresql:postgresql:42.2.8'
}
}

View File

@@ -40,7 +40,7 @@ import org.springframework.session.events.SessionDestroyedEvent;
*
* {@literal @Bean}
* public MapSessionRepository sessionRepository() {
* return new MapSessionRepository(new ConcurrentHashMap<>());
* return new MapSessionRepository();
* }
*
* }

View File

@@ -58,7 +58,7 @@ import org.springframework.util.ObjectUtils;
*
* {@literal @Bean}
* public MapSessionRepository sessionRepository() {
* return new MapSessionRepository(new ConcurrentHashMap<>());
* return new MapSessionRepository();
* }
*
* }

View File

@@ -36,7 +36,7 @@ import org.springframework.context.annotation.Import;
*
* {@literal @Bean}
* public ReactiveSessionRepository sessionRepository() {
* return new ReactiveMapSessionRepository(new ConcurrentHashMap<>());
* return new ReactiveMapSessionRepository();
* }
*
* }

View File

@@ -308,7 +308,7 @@ public class DefaultCookieSerializer implements CookieSerializer {
/**
* Sets the maxAge property of the Cookie. The default is to delete the cookie when
* the browser is closed.
* @param cookieMaxAge the maxAge property of the Cookie (defined in seconds)
* @param cookieMaxAge the maxAge property of the Cookie
*/
public void setCookieMaxAge(int cookieMaxAge) {
this.cookieMaxAge = cookieMaxAge;

View File

@@ -142,7 +142,7 @@ import org.springframework.util.Assert;
* <p>
* When a session is created an event is sent to Redis with the channel of
* "spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe" such that
* "33fdd1b6-b496-4b33-9f7d-df96679d32fe" is the session id. The body of the event will be
* "33fdd1b6-b496-4b33-9f7d-df96679d32fe" is the sesion id. The body of the event will be
* the session that was created.
* </p>
*

View File

@@ -25,22 +25,7 @@ dependencies {
def versions = dependencyManagement.managedVersions
asciidoctorPdf {
clearSources()
sources {
include "index.adoc"
}
}
asciidoctor {
clearSources()
sources {
include "index.adoc"
include "guides/*.adoc"
}
}
asciidoctorj {
def ghTag = snapshotBuild ? 'master' : project.version
def ghUrl = "https://github.com/spring-projects/spring-session/tree/$ghTag"
@@ -61,7 +46,5 @@ asciidoctorj {
'spring-session-version': project.version,
'version-milestone': milestoneBuild,
'version-release': releaseBuild,
'version-snapshot': snapshotBuild,
'highlightjsdir@': "js/highlight",
'docinfodir@': "."
'version-snapshot': snapshotBuild
}

View File

@@ -1,16 +1,11 @@
= Spring Session - find by username
Rob Winch
:toc: left
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
:toc:
This guide describes how to use Spring Session to find sessions by username.
NOTE: You can find the completed guide in the <<findbyusername-sample, findbyusername application>>.
[#index-link]
link:../index.html[Index]
[[findbyusername-assumptions]]
== Assumptions

View File

@@ -1,17 +1,11 @@
= Spring Session - Spring Boot
Rob Winch, Vedran Pavić
:toc: left
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
:toc:
This guide describes how to use Spring Session to transparently leverage a relational database to back a web application's `HttpSession` when you use Spring Boot.
NOTE: You can find the completed guide in the <<httpsession-jdbc-boot-sample, httpsession-jdbc-boot sample application>>.
[#index-link]
link:../index.html[Index]
== Updating Dependencies
Before you use Spring Session, you must update your dependencies.
@@ -52,9 +46,6 @@ spring.session.store-type=jdbc # Session store type.
----
====
If a single Spring Session module is present on the classpath, Spring Boot uses that store implementation automatically.
If you have more than one implementation, you must choose the StoreType that you wish to use to store the sessions, as shows above.
Under the hood, Spring Boot applies configuration that is equivalent to manually adding the `@EnableJdbcHttpSession` annotation.
This creates a Spring bean with the name of `springSessionRepositoryFilter`. That bean implements `Filter`.
The filter is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.

View File

@@ -1,17 +1,11 @@
= Spring Session - Spring Boot
Rob Winch, Vedran Pavić
:toc: left
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
:toc:
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when you use Spring Boot.
NOTE: You can find the completed guide in the <<boot-sample, boot sample application>>.
[#index-link]
link:../index.html[Index]
== Updating Dependencies
Before you use Spring Session, you must ensure your dependencies.
@@ -59,7 +53,7 @@ Further customization is possible by using `application.properties`, as the foll
.src/main/resources/application.properties
----
server.servlet.session.timeout= # Session timeout. If a duration suffix is not specified, seconds is used.
spring.session.redis.flush-mode=on_save # Sessions flush mode.
spring.session.redis.flush-mode=on-save # Sessions flush mode.
spring.session.redis.namespace=spring:session # Namespace for keys used to store sessions.
----
====
@@ -157,6 +151,6 @@ To do so, enter the following into your terminal, being sure to replace `7e8383a
----
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
----
====
=====
Now you can visit the application at http://localhost:8080/ and observe that we are no longer authenticated.

View File

@@ -1,66 +0,0 @@
= Spring Session - WebFlux with Custom Cookie
Eleftheria Stein-Kousathana
:toc: left
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
This guide describes how to configure Spring Session to use custom cookies in a WebFlux based application.
The guide assumes you have already set up Spring Session in your project using your chosen data store. For example, link:./boot-redis.html[HttpSession with Redis].
NOTE: You can find the completed guide in the <<webflux-custom-cookie-sample, WebFlux Custom Cookie sample application>>.
[#index-link]
link:../index.html[Index]
[[webflux-custom-cookie-spring-configuration]]
== Spring Boot Configuration
Once you have set up Spring Session, you can customize how the session cookie is written by exposing a `WebSessionIdResolver` as a Spring bean.
Spring Session uses a `CookieWebSessionIdResolver` by default.
Exposing the `WebSessionIdResolver` as a Spring bean augments the existing configuration when you use configurations like `@EnableRedisHttpSession`.
The following example shows how to customize Spring Session's cookie:
====
[source,java]
----
include::{samples-dir}spring-session-sample-boot-webflux-custom-cookie/src/main/java/sample/CookieConfig.java[tags=webflux-cookie-serializer]
----
<1> We customize the name of the cookie to be `JSESSIONID`.
<2> We customize the path of the cookie to be `/` (rather than the default of the context root).
<3> We customize the `SameSite` cookie directive to be `Strict`.
====
[[webflux-custom-cookie-sample]]
== `webflux-custom-cookie` Sample Application
This section describes how to work with the `webflux-custom-cookie` sample application.
=== Running the `webflux-custom-cookie` Sample Application
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
====
----
$ ./gradlew :spring-session-sample-boot-webflux-custom-cookie:bootRun
----
====
NOTE: For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
Alternatively, you can update the `RedisConnectionFactory` to point to a Redis server.
Another option is to use https://www.docker.com/[Docker] to run Redis on localhost. See https://hub.docker.com/_/redis/[Docker Redis repository] for detailed instructions.
You should now be able to access the application at http://localhost:8080/
=== Exploring the `webflux-custom-cookie` Sample Application
Now you can use the application. Fill out the form with the following information:
* *Attribute Name:* _username_
* *Attribute Value:* _rob_
Now click the *Set Attribute* button.
You should now see the values displayed in the table.
If you look at the cookies for the application, you can see the cookie is saved to the custom name of `JSESSIONID`.

View File

@@ -1,10 +1,7 @@
= Spring Session - WebSocket
Rob Winch
:toc: left
:toc:
:websocketdoc-test-dir: {docs-test-dir}docs/websocket/
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
This guide describes how to use Spring Session to ensure that WebSocket messages keep your HttpSession alive.
@@ -15,12 +12,9 @@ Specifically,it does not work with using https://www.jcp.org/en/jsr/detail?id=35
// end::disclaimer[]
[#index-link]
link:../index.html[Index]
== HttpSession Setup
The first step is to integrate Spring Session with the HttpSession. These steps are already outlined in the link:./boot-redis.html[HttpSession with Redis Guide].
The first step is to integrate Spring Session with the HttpSession. These steps are already outlined in the link:httpsession.html[HttpSession Guide].
Please make sure you have already integrated Spring Session with HttpSession before proceeding.

View File

@@ -1,2 +0,0 @@
<script type="text/javascript" src="../js/tocbot/tocbot.min.js"></script>
<script type="text/javascript" src="../js/toc.js"></script>

View File

@@ -1,18 +1,12 @@
= Spring Session - Custom Cookie
Rob Winch; Eleftheria Stein-Kousathana
:toc: left
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
:toc:
This guide describes how to configure Spring Session to use custom cookies with Java Configuration.
The guide assumes you have already set up Spring Session in your project using your chosen data store. For example, link:./boot-redis.html[HttpSession with Redis].
The guide assumes you have already link:./httpsession.html[set up Spring Session in your project].
NOTE: You can find the completed guide in the <<custom-cookie-sample, Custom Cookie sample application>>.
[#index-link]
link:../index.html[Index]
[[custom-cookie-spring-configuration]]
== Spring Java Configuration

View File

@@ -1,18 +1,12 @@
= Spring Session and Spring Security with Hazelcast
Tommy Ludwig; Rob Winch
:toc: left
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
:toc:
This guide describes how to use Spring Session along with Spring Security when you use Hazelcast as your data store.
It assumes that you have already applied Spring Security to your application.
NOTE: You cand find the completed guide in the <<hazelcast-spring-security-sample, Hazelcast Spring Security sample application>>.
[#index-link]
link:../index.html[Index]
== Updating Dependencies
Before you use Spring Session, you must update your dependencies.

View File

@@ -1,17 +1,11 @@
= Spring Session - HttpSession (Quick Start)
Rob Winch, Vedran Pavić
:toc: left
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
:toc:
This guide describes how to use Spring Session to transparently leverage a relational database to back a web application's `HttpSession` with Java Configuration.
NOTE: You can find the completed guide in the <<httpsession-jdbc-sample, httpsession-jdbc sample application>>.
[#index-link]
link:../index.html[Index]
== Updating Dependencies
Before you use Spring Session, you must update your dependencies.
@@ -105,7 +99,7 @@ For additional information on how to configure data access related concerns, see
== Java Servlet Container Initialization
Our <<httpsession-jdbc-spring-configuration,Spring Configuration>> created a Spring bean named `springSessionRepositoryFilter` that implements `Filter`.
Our <<httpsession-spring-configuration,Spring Configuration>> 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.

View File

@@ -1,18 +1,12 @@
= Spring Session - HttpSession (Quick Start)
Rob Winch
:toc: left
:toc:
:version-snapshot: true
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` with Java Configuration.
NOTE: You can find the completed guide in the <<httpsession-sample, httpsession sample application>>.
[#index-link]
link:../index.html[Index]
== Updating Dependencies
Before you use Spring Session, you must update your dependencies.
If you are using Maven, you must add the following dependencies:

View File

@@ -1,17 +1,11 @@
= Spring Session - REST
Rob Winch
:toc: left
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
:toc:
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when you use REST endpoints.
NOTE: You can find the completed guide in the <<rest-sample, rest sample application>>.
[#index-link]
link:../index.html[Index]
== Updating Dependencies
Before you use Spring Session, you must update your dependencies.
@@ -247,7 +241,7 @@ $ curl -v http://localhost:8080/ -u user:password
In the output, you should notice the following:
====
===
----
HTTP/1.1 200 OK
...

View File

@@ -1,18 +1,12 @@
= Spring Session and Spring Security
Rob Winch
:toc: left
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
:toc:
This guide describes how to use Spring Session along with Spring Security.
It assumes you have already applied Spring Security to your application.
NOTE: You can find the completed guide in the <<security-sample, security sample application>>.
[#index-link]
link:../index.html[Index]
== Updating Dependencies
Before you use Spring Session, you must update your dependencies.
If you use Maven, you must add the following dependencies:

View File

@@ -1,17 +1,11 @@
= Spring Session - HttpSession (Quick Start)
Rob Winch, Vedran Pavić
:toc: left
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
:toc:
This guide describes how to use Spring Session to transparently leverage a relational to back a web application's `HttpSession` with XML based configuration.
NOTE: You can find the completed guide in the <<httpsession-jdbc-xml-sample, httpsession-jdbc-xml sample application>>.
[#index-link]
link:../index.html[Index]
== Updating Dependencies
Before you use Spring Session, you must update your dependencies.
@@ -74,8 +68,8 @@ You must have the following in your pom.xml:
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
====
endif::[]
====
// tag::config[]
@@ -107,7 +101,7 @@ For additional information on how to configure data access-related concerns, see
== XML Servlet Container Initialization
Our <<httpsession-jdbc-xml-spring-configuration,Spring Configuration>> created a Spring bean named `springSessionRepositoryFilter` that implements `Filter`.
Our <<httpsession-xml-spring-configuration,Spring Configuration>> 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, we need to instruct Spring to load our `session.xml` configuration.

View File

@@ -1,17 +1,11 @@
= Spring Session - HttpSession (Quick Start)
Rob Winch
:toc: left
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides
:toc:
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` with XML-based configuration.
NOTE: You can find the completed guide in the <<httpsession-xml-sample, httpsession-xml sample application>>.
[#index-link]
link:../index.html[Index]
== Updating Dependencies
Before you use Spring Session, you must update your dependencies.
If you use Maven, you must add the following dependencies:

View File

@@ -70,10 +70,6 @@ To get started with Spring Session, the best place to start is our Sample Applic
| Demonstrates how to use Spring Session to replace the Spring WebFlux's `WebSession` with Redis.
|
| {gh-samples-url}spring-session-sample-boot-webflux-custom-cookie[WebFlux with Custom Cookie]
| Demonstrates how to use Spring Session to customize the Session cookie in a WebFlux based application.
| link:guides/boot-webflux-custom-cookie.html[WebFlux with Custom Cookie Guide]
| {gh-samples-url}spring-session-sample-boot-redis-json[HttpSession with Redis JSON serialization]
| Demonstrates how to use Spring Session to replace the `HttpSession` with Redis using JSON serialization.
|
@@ -293,7 +289,7 @@ Any method that returns an `HttpSession` is overridden.
All other methods are implemented by `HttpServletRequestWrapper` and delegate to the original `HttpServletRequest` implementation.
We replace the `HttpServletRequest` implementation by using a servlet `Filter` called `SessionRepositoryFilter`.
The following pseudocode shows how it works:
The pseudocode belows:
====
[source, java]

View File

@@ -19,7 +19,6 @@ package docs.security;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@@ -42,16 +41,14 @@ public class RememberMeSecurityConfiguration extends WebSecurityConfigurerAdapte
protected void configure(HttpSecurity http) throws Exception {
http
// ... additional configuration ...
.rememberMe((rememberMe) -> rememberMe
.rememberMeServices(rememberMeServices())
);
.rememberMe()
.rememberMeServices(rememberMeServices());
// end::http-rememberme[]
http
.formLogin(Customizer.withDefaults())
.authorizeRequests((authorize) -> authorize
.anyRequest().authenticated()
);
.formLogin().and()
.authorizeRequests()
.anyRequest().authenticated();
}
// tag::rememberme-bean[]

View File

@@ -40,10 +40,9 @@ public class SecurityConfiguration<S extends Session> extends WebSecurityConfigu
// @formatter:off
http
// other config goes here...
.sessionManagement((sessionManagement) -> sessionManagement
.sessionManagement()
.maximumSessions(2)
.sessionRegistry(sessionRegistry())
);
.sessionRegistry(sessionRegistry());
// @formatter:on
}

View File

@@ -16,7 +16,7 @@ dependencies {
integrationTestCompile "com.h2database:h2"
integrationTestCompile "com.ibm.db2:jcc"
integrationTestCompile "com.microsoft.sqlserver:mssql-jdbc"
integrationTestCompile "com.oracle.database.jdbc:ojdbc8"
integrationTestCompile "com.oracle.ojdbc:ojdbc8"
integrationTestCompile "com.zaxxer:HikariCP"
integrationTestCompile "mysql:mysql-connector-java"
integrationTestCompile "org.apache.derby:derby"

View File

@@ -103,7 +103,7 @@ import org.springframework.util.StringUtils;
* );
*
* CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
* CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
* CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (EXPIRY_TIME);
* CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);
*
* CREATE TABLE SPRING_SESSION_ATTRIBUTES (

View File

@@ -1,23 +1,23 @@
dependencyManagement {
imports {
mavenBom 'com.fasterxml.jackson:jackson-bom:2.11.0'
mavenBom 'com.fasterxml.jackson:jackson-bom:2.10.0'
}
dependencies {
dependency 'ch.qos.logback:logback-classic:1.2.3'
dependency 'com.maxmind.geoip2:geoip2:2.14.0'
dependency 'com.maxmind.geoip2:geoip2:2.3.1'
dependency 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2'
dependency 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.3'
dependency 'org.apache.taglibs:taglibs-standard-jstlel:1.2.5'
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.36.0'
dependency 'org.slf4j:jcl-over-slf4j:1.7.30'
dependency 'org.slf4j:log4j-over-slf4j:1.7.30'
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.33.0'
dependency 'org.slf4j:jcl-over-slf4j:1.7.28'
dependency 'org.slf4j:log4j-over-slf4j:1.7.28'
dependency 'org.webjars:bootstrap:2.3.2'
dependency 'org.webjars:html5shiv:3.7.3'
dependency 'org.webjars:jquery:1.12.4'
dependency 'org.webjars:knockout:2.3.0'
dependency 'org.webjars:sockjs-client:0.3.4'
dependency 'org.webjars:stomp-websocket:2.3.3'
dependency 'org.webjars:stomp-websocket:2.3.0'
dependency 'org.webjars:webjars-taglib:0.3'
}
}

View File

@@ -53,8 +53,6 @@ class FindByUsernameTests {
private WebDriver driver;
private WebDriver driver2;
@BeforeEach
void setup() {
this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).build();
@@ -63,9 +61,6 @@ class FindByUsernameTests {
@AfterEach
void tearDown() {
this.driver.quit();
if (this.driver2 != null) {
this.driver2.quit();
}
}
@Test
@@ -84,25 +79,6 @@ class FindByUsernameTests {
home.terminateButtonDisabled();
}
@Test
void terminateOtherSession() throws Exception {
HomePage forgotToLogout = home(this.driver);
this.driver2 = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).build();
HomePage terminateFogotSession = home(this.driver2);
terminateFogotSession.terminateSession(forgotToLogout.getSessionId()).assertAt();
LoginPage login = HomePage.go(this.driver);
login.assertAt();
}
private static HomePage home(WebDriver driver) {
LoginPage login = HomePage.go(driver);
HomePage home = login.form().login(HomePage.class);
home.assertAt();
return home;
}
@TestConfiguration
static class Config {

View File

@@ -56,18 +56,6 @@ public class HomePage extends BasePage {
}
public void terminateButtonDisabled() {
String sessionId = getSessionId();
WebElement element = getDriver().findElement(By.id("terminate-" + sessionId));
assertThat(element.isEnabled()).isFalse();
}
public HomePage terminateSession(String sessionId) {
WebElement terminate = getDriver().findElement(By.id("terminate-" + sessionId));
terminate.click();
return new HomePage(getDriver());
}
public String getSessionId() {
Set<Cookie> cookies = getDriver().manage().getCookies();
String cookieValue = null;
for (Cookie cookie : cookies) {
@@ -75,7 +63,8 @@ public class HomePage extends BasePage {
cookieValue = new String(Base64.getDecoder().decode(cookie.getValue()));
}
}
return cookieValue;
WebElement element = getDriver().findElement(By.id("terminate-" + cookieValue));
assertThat(element.isEnabled()).isFalse();
}
public HomePage logout() {

View File

@@ -35,14 +35,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorize) -> authorize
.authorizeRequests()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)
.formLogin((formLogin) -> formLogin
.and()
.formLogin()
.loginPage("/login")
.permitAll()
);
.permitAll();
}
// end::config[]
// @formatter:on

View File

@@ -26,8 +26,8 @@ import org.springframework.session.Session;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Controller for sending the user to the login view.
@@ -50,7 +50,7 @@ public class IndexController {
}
// end::findbyusername[]
@PostMapping("/sessions/{sessionIdToDelete}")
@RequestMapping(value = "/sessions/{sessionIdToDelete}", method = RequestMethod.DELETE)
public String removeSession(Principal principal, @PathVariable String sessionIdToDelete) {
Set<String> usersSessionIds = this.sessions.findByPrincipalName(principal.getName()).keySet();
if (usersSessionIds.contains(sessionIdToDelete)) {

View File

@@ -25,7 +25,7 @@
<td th:text="${#temporals.format(sessionElement.lastAccessedTime.atZone(T(java.time.ZoneId).systemDefault()),'dd/MMM/yyyy HH:mm:ss')}"></td>
<td th:text="${details?.accessType}"></td>
<td>
<form th:action="@{'/sessions/' + ${sessionElement.id}}" th:method="post">
<form th:action="@{'/sessions/' + ${sessionElement.id}}" th:method="delete">
<input th:id="'terminate-' + ${sessionElement.id}" type="submit" value="Terminate" th:disabled="${sessionElement.id == #httpSession.id}"/>
</form>
</td>

View File

@@ -44,13 +44,12 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorize) -> authorize
.authorizeRequests()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)
.formLogin((formLogin) -> formLogin
.permitAll()
);
.and()
.formLogin()
.permitAll();
}
// end::config[]
// @formatter:on

View File

@@ -34,14 +34,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorize) -> authorize
.authorizeRequests()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)
.formLogin((formLogin) -> formLogin
.and()
.formLogin()
.loginPage("/login")
.permitAll()
);
.permitAll();
}
// @formatter:on

View File

@@ -28,13 +28,12 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorize) -> authorize
.authorizeRequests()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)
.formLogin((formLogin) -> formLogin
.permitAll()
);
.and()
.formLogin()
.permitAll();
}
// @formatter:on

View File

@@ -35,13 +35,12 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorize) -> authorize
.authorizeRequests()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)
.formLogin((formLogin) -> formLogin
.permitAll()
);
.and()
.formLogin()
.permitAll();
}
// end::config[]
// @formatter:on

View File

@@ -1,17 +0,0 @@
apply plugin: 'io.spring.convention.spring-sample-boot'
dependencies {
compile project(':spring-session-data-redis')
compile "org.springframework.boot:spring-boot-starter-webflux"
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
compile "org.springframework.boot:spring-boot-starter-data-redis"
compile "org.springframework.boot:spring-boot-devtools"
compile 'org.webjars:bootstrap'
testCompile "org.springframework.boot:spring-boot-starter-test"
testCompile "org.junit.jupiter:junit-jupiter-api"
testRuntime "org.junit.jupiter:junit-jupiter-engine"
integrationTestCompile seleniumDependencies
integrationTestCompile "org.testcontainers:testcontainers"
}

View File

@@ -1,112 +0,0 @@
/*
* Copyright 2014-2020 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
*
* https://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 java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.testcontainers.containers.GenericContainer;
import sample.pages.HomePage;
import sample.pages.HomePage.Attribute;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eleftheria Stein
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class AttributeTests {
private static final String DOCKER_IMAGE = "redis:5.0.9";
@LocalServerPort
private int port;
private WebDriver driver;
@BeforeEach
void setup() {
this.driver = new HtmlUnitDriver();
}
@AfterEach
void tearDown() {
this.driver.quit();
}
@Test
void home() {
HomePage home = HomePage.go(this.driver, this.port);
home.assertAt();
}
@Test
void noAttributes() {
HomePage home = HomePage.go(this.driver, this.port);
assertThat(home.attributes()).isEmpty();
}
@Test
void createAttribute() {
HomePage home = HomePage.go(this.driver, this.port);
// @formatter:off
home = home.form()
.attributeName("a")
.attributeValue("b")
.submit(HomePage.class);
// @formatter:on
List<Attribute> attributes = home.attributes();
assertThat(attributes).hasSize(1);
Attribute row = attributes.get(0);
assertThat(row.getAttributeName()).isEqualTo("a");
assertThat(row.getAttributeValue()).isEqualTo("b");
}
@TestConfiguration
static class Config {
@Bean
GenericContainer redisContainer() {
GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE).withExposedPorts(6379);
redisContainer.start();
return redisContainer;
}
@Bean
LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(redisContainer().getContainerIpAddress(),
redisContainer().getFirstMappedPort());
}
}
}

View File

@@ -1,138 +0,0 @@
/*
* Copyright 2014-2020 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
*
* https://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 java.util.ArrayList;
import java.util.List;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eleftheria Stein
*/
public class HomePage {
private WebDriver driver;
@FindBy(css = "form")
WebElement form;
@FindBy(css = "table tbody tr")
List<WebElement> trs;
List<Attribute> attributes;
public HomePage(WebDriver driver) {
this.driver = driver;
this.attributes = new ArrayList<>();
}
private static void get(WebDriver driver, int port, String get) {
String baseUrl = "http://localhost:" + port;
driver.get(baseUrl + get);
}
public static HomePage go(WebDriver driver, int port) {
get(driver, port, "/");
return PageFactory.initElements(driver, HomePage.class);
}
public void assertAt() {
assertThat(this.driver.getTitle()).isEqualTo("Session Attributes");
}
public List<Attribute> attributes() {
List<Attribute> rows = new ArrayList<>();
for (WebElement tr : this.trs) {
rows.add(new Attribute(tr));
}
this.attributes.addAll(rows);
return this.attributes;
}
public Form form() {
return new Form(this.form);
}
public class Form {
@FindBy(name = "attributeName")
WebElement attributeName;
@FindBy(name = "attributeValue")
WebElement attributeValue;
@FindBy(css = "input[type=\"submit\"]")
WebElement submit;
public Form(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
public Form attributeName(String text) {
this.attributeName.sendKeys(text);
return this;
}
public Form attributeValue(String text) {
this.attributeValue.sendKeys(text);
return this;
}
public <T> T submit(Class<T> page) {
this.submit.click();
return PageFactory.initElements(HomePage.this.driver, page);
}
}
public static class Attribute {
@FindBy(xpath = ".//td[1]")
WebElement attributeName;
@FindBy(xpath = ".//td[2]")
WebElement attributeValue;
public Attribute(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
/**
* @return the attributeName
*/
public String getAttributeName() {
return this.attributeName.getText();
}
/**
* @return the attributeValue
*/
public String getAttributeValue() {
return this.attributeValue.getText();
}
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2014-2020 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
*
* https://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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.session.CookieWebSessionIdResolver;
import org.springframework.web.server.session.WebSessionIdResolver;
/**
* @author Eleftheria Stein
*/
@Configuration
public class CookieConfig {
// tag::webflux-cookie-serializer[]
@Bean
public WebSessionIdResolver webSessionIdResolver() {
CookieWebSessionIdResolver resolver = new CookieWebSessionIdResolver();
resolver.setCookieName("JSESSIONID"); // <1>
resolver.addCookieInitializer((builder) -> builder.path("/")); // <2>
resolver.addCookieInitializer((builder) -> builder.sameSite("Strict")); // <3>
return resolver;
}
// end::webflux-cookie-serializer[]
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2014-2020 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
*
* https://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 Eleftheria Stein
*/
@SpringBootApplication
public class HelloWebFluxApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWebFluxApplication.class, args);
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2014-2020 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
*
* https://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;
/**
* @author Eleftheria Stein
*/
public class SessionAttributeForm {
private String attributeName;
private String attributeValue;
public String getAttributeName() {
return this.attributeName;
}
public void setAttributeName(String attributeName) {
this.attributeName = attributeName;
}
public String getAttributeValue() {
return this.attributeValue;
}
public void setAttributeValue(String attributeValue) {
this.attributeValue = attributeValue;
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2014-2020 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
*
* https://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.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.server.WebSession;
// tag::class[]
@Controller
public class SessionController {
@PostMapping("/session")
public String setAttribute(@ModelAttribute SessionAttributeForm sessionAttributeForm, WebSession session) {
session.getAttributes().put(sessionAttributeForm.getAttributeName(), sessionAttributeForm.getAttributeValue());
return "redirect:/";
}
@GetMapping("/")
public String index(Model model, WebSession webSession) {
model.addAttribute("webSession", webSession);
return "home";
}
}
// tag::end[]

View File

@@ -1,42 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Session Attributes</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div class="container">
<h1>Description</h1>
<p>This application demonstrates how to customize the session cookie. Notice that the name of the cookie is JSESSIONID.</p>
<h1>Try it</h1>
<form class="form-inline" role="form" action="./session" method="post">
<label for="attributeName">Attribute Name</label>
<input id="attributeName" type="text" name="attributeName"/>
<label for="attributeValue">Attribute Value</label>
<input id="attributeValue" type="text" name="attributeValue"/>
<input type="submit" value="Set Attribute"/>
</form>
<hr/>
<table class="table table-striped">
<thead>
<tr>
<th>Attribute Name</th>
<th>Attribute Value</th>
</tr>
</thead>
<tbody>
<tr th:each="attr : ${webSession.attributes}">
<td th:text="${attr.key}"/></td>
<td th:text="${attr.value}"/></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -36,7 +36,7 @@ public class SessionController {
@GetMapping("/")
public String index(Model model, WebSession webSession) {
model.addAttribute("webSession", webSession);
return "home";
return "index";
}
private static final long serialVersionUID = 2878267318695777395L;

View File

@@ -8,7 +8,6 @@ dependencies {
compile "org.springframework.boot:spring-boot-starter-data-jpa"
compile "org.springframework.boot:spring-boot-starter-data-redis"
compile "org.springframework.boot:spring-boot-starter-websocket"
compile "org.springframework.boot:spring-boot-starter-validation"
compile "org.springframework.boot:spring-boot-devtools"
compile "org.springframework:spring-websocket"
compile "org.springframework.security:spring-security-messaging"

View File

@@ -53,13 +53,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorize) -> authorize
.authorizeRequests()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)
.formLogin((formLogin) -> formLogin
.permitAll()
);
.and()
.formLogin()
.permitAll();
}
// @formatter:on

View File

@@ -37,4 +37,4 @@ public class SessionServlet extends HttpServlet {
private static final long serialVersionUID = 2878267318695777395L;
}
// end::class[]
// tag::end[]

View File

@@ -38,4 +38,4 @@ public class SessionServlet extends HttpServlet {
private static final long serialVersionUID = 2878267318695777395L;
}
// end::class[]
// tag::end[]

View File

@@ -22,5 +22,4 @@ dependencies {
gretty {
jvmArgs = ['-Dspring.profiles.active=embedded-redis']
servletContainer = 'tomcat9'
}

View File

@@ -17,7 +17,6 @@
package sample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -32,13 +31,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorize) -> authorize
.authorizeRequests()
.anyRequest().authenticated()
)
.requestCache((requestCache) -> requestCache
.and()
.requestCache()
.requestCache(new NullRequestCache())
)
.httpBasic(Customizer.withDefaults());
.and()
.httpBasic();
}
// @formatter:on