Compare commits

...

583 Commits

Author SHA1 Message Date
Rob Winch
33fbaa03a8 Release 2.0.0.M5 2017-10-09 16:54:31 -05:00
Rob Winch
88b26f2cfe Update to Spring Security 5.0.0.M5
Fixes gh-891
2017-10-09 16:53:41 -05:00
Vedran Pavic
3f670050ef Update integration tests
This commit updates versions of RDBMS and Redis Docker images used in
integration tests.

Closes gh-894
2017-10-09 08:09:52 +02:00
Vedran Pavic
e3b61d25bb Improve JDBC configuration
This commit improves JDBC configuration by introducing `@SpringSessionDataSource` qualifier for explicitly declaring a `DataSource` to be used by Spring Session. This is in particular useful in scenarios with multiple `DataSource` beans present in the application context.

As a consequence, JDBC configuration is simplified and no longer registers a Spring Session specific `JdbcTemplate` bean.

Closes gh-863
2017-10-06 19:12:55 +02:00
Vedran Pavic
19b8effa41 Add Redis implementation of ReactorSessionRepository
Closes gh-816
2017-10-06 18:45:42 +02:00
Vedran Pavic
9f5f7540d2 Fix Users sample app navbar
Closes gh-885
2017-10-02 22:31:55 +02:00
Vedran Pavic
eb8c22939c Upgrade Gradle to 4.2.1 2017-10-02 21:20:14 +02:00
Vedran Pavic
45cfa1e9a4 Upgrade spring-build-conventions to 0.0.4.RELEASE 2017-10-02 20:53:06 +02:00
Vedran Pavic
99221e0948 Upgrade dependencies
This commit harmonizes project dependencies with Spring IO Platform Cairo levels.
2017-10-02 19:01:47 +02:00
Vedran Pavic
41cf2ef152 Update documentation to reflect preference for Lettuce
See gh-886
2017-10-02 19:00:23 +02:00
Vedran Pavic
c51bce4777 Use Lettuce driver for integration tests
Closes gh-886
2017-09-28 16:17:55 +02:00
Vedran Pavic
b6f1184c4c Upgrade dependencies
This commit harmonizes project dependencies with Spring IO Platform Cairo levels.
2017-09-28 15:51:32 +02:00
Vedran Pavic
c69a8b8762 Improve JDBC data store schema scripts
Closes gh-884
2017-09-27 12:18:25 +02:00
Vedran Pavic
99fb17a66b Adapt to Spring WebSession API changes 2017-09-27 11:52:04 +02:00
Vedran Pavic
937b2fcbf1 Upgrade Gradle to 4.2 2017-09-25 09:06:06 +02:00
Vedran Pavic
9c5a7e9156 Upgrade Spring Boot to 2.0.0.M4
Closes gh-877
2017-09-15 22:13:23 +02:00
Vedran Pavic
4deccd3ad0 Upgrade Gradle to 4.1 2017-09-15 21:24:48 +02:00
Vedran Pavic
da058e9510 Upgrade dependencies to latest snapshots
- Reactor Bismuth-BUILD-SNAPSHOT
- Spring Framework 5.0.0.BUILD-SNAPSHOT
- Spring Data Kay-BUILD-SNAPSHOT
- Spring Security 5.0.0.BUILD-SNAPSHOT
2017-09-14 07:18:24 +02:00
Vedran Pavic
d28ca4658b Next development version 2017-09-14 07:16:49 +02:00
Rob Winch
c14fdb283d Release 2.0.0.M4 2017-09-13 18:04:06 -05:00
Rob Winch
ee1ff3ed3b Update Spring Security 5.0.0.M4 2017-09-13 18:03:33 -05:00
Vedran Pavic
eb7bcc5eeb Harmonize ReactorSessionRepository API
This commit renames the `ReactorSessionRepository#delete` to `deleteById` in order to make API consistent with `SessionRepository`.
2017-09-12 23:00:16 +02:00
Vedran Pavic
188e5ba4e0 Optimize JDBC session cleanup SQL statement
This commit improves session cleanup handling in  `JdbcOperationsSessionRepository#cleanUpExpiredSessions` by optimizing the used SQL statement. This is done by calculating the session expiry time when persisting the session, which in turn allows the cleanup SQL statement to be more index-friendly.

Closes gh-847
2017-09-12 15:41:10 -05:00
Vedran Pavic
1e46630467 Remove MapReactorSessionRepository default constructor
This commit removes the default `MapReactorSessionRepository` so that the users are required to explicitly supply the `Map` used to store the sessions.
2017-09-12 15:22:29 -05:00
Vedran Pavic
b72c600884 Upgrade dependencies to current milestones
- Reactor Bismuth-M4
- Spring Framework 5.0.0.RC4
- Spring Data Kay-RC3
- Lettuce 5.0.0.RC2
2017-09-12 07:54:14 +02:00
Vedran Pavic
274aec1691 Fix Boot based samples 2017-09-11 13:57:38 +02:00
Rob Winch
52ea98b4ce SpringWebSessionConfigurationTests close ApplicationContext 2017-09-07 20:12:45 -05:00
Rob Winch
5c294ae1d2 Polish 2017-09-07 20:12:45 -05:00
Greg Turnquist
1752928d96 Configure WebSessionManager's WebSessionIdResolver by bean definition
Allow a WebSessionIdResolver registered as a Spring bean to be wired into the WebSessionManager.
2017-09-07 20:12:45 -05:00
Vedran Pavic
0cdee25405 Remove MapSessionRepository default constructor
This commit removes the default `MapSessionRepository` constructor so that the users are required to explicitly supply the `Map` used to store the sessions.
2017-09-07 19:58:22 -05:00
Rob Winch
4a9f1700d5 Polish
Reorder methods
2017-09-06 15:12:13 -05:00
Rob Winch
36ab358d24 Remove SpringSessionWebSessionManager
Spring's DefaultWebSessionManager now supports all the functionality
that is needed for Spring Session, so we only need to implement
WebSessionStore
2017-09-06 14:50:48 -05:00
Vedran Pavic
8e3371aed9 Allow easier customization of cookie max age logic 2017-08-24 16:00:59 -05:00
mikemassa84
2161f966de Update grails3.adoc
Add a note about spring-session and grails flash scope, with link to stackoverflow answer.
2017-08-24 14:37:32 -05:00
Vedran Pavic
63b67a501d Update guides for Redis based samples 2017-08-24 14:17:10 -05:00
Vedran Pavic
2b0431eae4 Use TestContainers for Gretty integration tests 2017-08-24 14:17:10 -05:00
Vedran Pavic
04ec086014 Use TestContainers for integration tests 2017-08-24 14:17:10 -05:00
Rob Winch
5697f49a71 Config->HelloWebfluxSessionConfig
Use a more meaningful name
2017-08-24 13:54:21 -05:00
Rob Winch
dfce66383f webflux sample uses @EnableSpringWebSession
Issue gh-861
2017-08-24 13:52:52 -05:00
Rob Winch
a83e59bf52 Polish
Fix checkstyle

Issue gh-861
2017-08-24 13:52:24 -05:00
Greg Turnquist
8b233e84ef Create @EnableSpringWebSession annotation. 2017-08-24 13:41:42 -05:00
Greg Turnquist
84e7fbace1 Fix MapReactorSessionRepository's delete(). 2017-08-24 13:40:57 -05:00
Rob Winch
f455df3333 Add WebFlux sample
Fixesh gh-857
2017-08-18 16:28:46 -05:00
Rob Winch
a7bb9d3b31 SpringSessionWebSessionManager writes on commit
when the ServerHttpResonse is commited the cookie and the session are written

Fixes gh-856
2017-08-18 16:28:46 -05:00
Kanjie Lu
5f0e4c3b85 fix typo
change "they key" to  "the key"
2017-08-05 20:39:08 -05:00
Vedran Pavic
23c6c7cf31 Upgrade Spring Boot to 2.0.0.M3
Closes gh-841
2017-07-27 14:08:48 +02:00
Vedran Pavic
c8c5fae678 Polish build 2017-07-26 08:49:45 +02:00
Vedran Pavic
f4a58622e4 Upgrade dependencies to latest snapshots
- Reactor Bismuth-BUILD-SNAPSHOT
- Spring Framework 5.0.0.BUILD-SNAPSHOT
- Spring Data Kay-BUILD-SNAPSHOT
- Spring Security 5.0.0.BUILD-SNAPSHOT
2017-07-26 08:47:23 +02:00
Joe Grandja
5384764021 Next development version 2017-07-25 10:12:45 -04:00
Joe Grandja
56033a9b68 Release version 2.0.0.M3 2017-07-25 09:31:12 -04:00
Vedran Pavic
99a2b079ac Upgrade dependencies to current milestones
- Spring Framework 5.0.0.RC3
- Spring Data Kay-RC1
- Spring Security 5.0.0.M3
2017-07-25 10:11:26 +02:00
Vedran Pavic
9120151692 Polish "Add WebFlux Support"
Closes gh-683
2017-07-21 15:31:45 +02:00
Rob Winch
5abbe66b1d Add WebFlux Support 2017-07-21 15:30:19 +02:00
Rob Winch
f00c196430 Update Dependencies to Prepare Release 2017-07-20 22:24:35 -05:00
Rob Winch
be2604ca69 Add Session.changeSessionId 2017-07-20 16:31:38 -05:00
Rob Winch
2aa71ffb6d Update to lettuce 5.0.0.RC1 2017-07-20 09:54:53 -05:00
Rob Winch
8bdcba6e50 Spring Session 2.0.0.M2 w/ Boot Samples
This is necessary because the current version of Spring Session is not
compatible with Boot 2.0.0.M2 and we cannot release against the SNAPSHOTs

Issue gh-833
2017-07-18 20:09:21 -05:00
Mark Paluch
8dd1a10f1b Adapt to changes in Spring Data Redis API 2017-07-18 16:19:05 +02:00
Vedran Pavic
1d247aa96f Align Checkstyle config with spring-build-conventions 2017-07-12 08:24:09 +02:00
Vedran Pavic
c00d6a7bf2 Apply correct convention plugin for modules 2017-07-10 09:10:33 +02:00
Vedran Pavic
c0df3bf28b Fix deprecation warnings 2017-07-10 07:28:09 +02:00
Vedran Pavic
1b8c9838a4 Fix unchecked operations build warning 2017-07-10 07:27:27 +02:00
Vedran Pavic
8a1b454121 Fix javadoc build warning 2017-07-10 07:26:35 +02:00
Vedran Pavic
ef69c8169a Polish dependencies
This commit removes needless dependency exclusions.

See gh-824
2017-07-10 07:20:35 +02:00
Rob Winch
40b3d07224 Revert "Add --debug to Jenkinsfile"
This reverts commit 9c4e20f074.
2017-07-07 11:23:54 -05:00
Rob Winch
8c726f2215 Use Gradle 3.5.1
This works around a bug in 4.0 where Gradle is hanging when trying to
resolve a configuration.
2017-07-07 11:17:32 -05:00
Rob Winch
c2a86a27ce Travis skip install 2017-07-07 10:16:13 -05:00
Rob Winch
6a08ef6f97 Polish travis build 2017-07-07 10:03:54 -05:00
Rob Winch
9c4e20f074 Add --debug to Jenkinsfile
Try and troubleshoot why the build is hanging
2017-07-07 09:42:24 -05:00
Vedran Pavic
5845a9c46a Improve dependency management
This commit improves dependency management with the following changes:

 - `spring-session-core`: move `javax.servlet-api` from `provided` to `optional` configuration due to introduction of reactive support
 - `spring-session-data-redis`: remove Redis driver from `compile` configuration
 - Boot samples: delegate Redis driver choice to `spring-boot-starter-data-redis`
 - polish `test` configuration dependencies
2017-07-06 17:05:01 -05:00
Rob Winch
7c6693a268 Remove Sonar to see if it fixes build 2017-07-06 08:49:21 -05:00
Vedran Pavic
05a3f59813 Simplify Hazelcast sample 2017-07-05 16:00:28 -05:00
Vedran Pavic
47a7a35aa4 Remove use of Assert#notNull from core components
Fixes gh-820
2017-07-05 17:04:03 +02:00
Vedran Pavic
04b4fe3e3b Fix Checkstyle violations 2017-07-01 00:04:46 +02:00
Rob Winch
36bb65e4b5 Add default methods to Session
Fixes gh-819
2017-06-30 10:25:14 -05:00
Rob Winch
8ef36e4f3e Session Optional<T> getAttribute -> T getAttribute
Issue gh-819
2017-06-30 10:24:59 -05:00
Rob Winch
ab3e280993 Update to latest SNAPSHOTs 2017-06-29 22:20:26 -05:00
Rob Winch
30562b5749 Use Spring IO Cairo-BUILD-SNAPSHOT 2017-06-27 16:37:13 -05:00
Rob Winch
d42a7b65ea Add MapReactorSessionRepository
Fixes gh-815
2017-06-27 16:32:51 -05:00
Rob Winch
db9807d12b Add ReactorSessionRepository
Fixes gh-814
2017-06-27 16:32:51 -05:00
Rob Winch
db09fa8168 Use SPRING_SESSION_TEAM_EMAILS 2017-06-26 10:52:17 -05:00
Vedran Pavic
031541bc05 Fix Checkstyle violations 2017-06-26 08:45:20 +02:00
Vedran Pavic
084e3428fb Move SessionEventRegistry back to integration-test sources
Fixes gh-810
2017-06-23 19:03:46 +02:00
Vedran Pavic
b321ff02f0 Revert "SessionRepository.save returns S"
See gh-809
2017-06-23 18:48:30 +02:00
Rob Winch
c6c6beb40c Session.delete -> deleteById
Fixes gh-809
2017-06-22 21:30:14 -05:00
Rob Winch
0127ef9f9b SessionRepository.getSession(String) -> findById(String)
Issue gh-809
2017-06-22 21:29:34 -05:00
Rob Winch
cd8686ae9c SessionRepository.save returns S
Issue gh-809
2017-06-22 21:27:25 -05:00
Rob Winch
233d179bfa Revert "Allow Publishing When Spring IO Fails"
This reverts commit fca411996a.
2017-06-22 13:20:23 -05:00
Rob Winch
4e8ae8d9d4 Revert "Deploy Without Checks for Spring IO"
This reverts commit 8c6810c6dd.
2017-06-22 13:20:21 -05:00
Rob Winch
8c6810c6dd Deploy Without Checks for Spring IO 2017-06-22 13:18:01 -05:00
Rob Winch
fca411996a Allow Publishing When Spring IO Fails
This is necessary to solve the problem of chicken and the Egg. See
https://github.com/spring-io/platform/issues/622#issuecomment-310452646
2017-06-22 13:00:12 -05:00
Rob Winch
79b8296e1c Work Around spring-projects/spring-boot#9573
Issue gh-806
2017-06-22 09:04:23 -05:00
Rob Winch
043cb42149 Extract spring-session-jdbc
Issue gh-806
2017-06-22 09:04:09 -05:00
Rob Winch
c28f047eb5 Extract spring-session-data-hazelcast
Issue gh-806
2017-06-22 09:03:50 -05:00
Rob Winch
972cf66d7e Extract spring-session-data-redis
Issue gh-806
2017-06-22 09:03:25 -05:00
Rob Winch
f1319483ee Move spring-session to spring-session-core
Issue gh-806
2017-06-22 09:02:44 -05:00
Rob Winch
6ad5006280 Update to Gradle 4.0 2017-06-16 13:40:43 -05:00
Vedran Pavic
f7e07b7f6b Improve Session API to use Java 8 2017-06-16 11:44:19 -05:00
Vedran Pavic
4cf26d9c36 Move ExpiringSession API into Session 2017-06-16 11:44:19 -05:00
Vedran Pavic
a848df1235 Replace explicit type arguments with diamond operator 2017-06-16 11:44:19 -05:00
Rob Winch
f8292ba512 Update to Spring Boot 2.0.0.M2
Fixes gh-801
2017-06-16 10:29:30 -05:00
Rob Winch
21bcc6e8d7 Next Development Version 2017-06-15 20:35:20 -05:00
Rob Winch
905a77a3a8 Release 2.0.0.M2 2017-06-15 20:08:29 -05:00
Rob Winch
295f9f78c3 Update to latest Releases
- Spring Framework 5.0.0.RC2
- Spring Security 5.0.0.M2
- Spring Data Kay-M4
2017-06-15 20:08:15 -05:00
Rob Winch
04ecc82d09 Polish Artifactory Publish 2017-06-15 11:40:04 -05:00
Rob Winch
2ddd9e58a3 Update to spring-build-conventions 0.0.2.RELEASE 2017-06-15 11:39:21 -05:00
Vedran Pavic
3c52298c47 Add support for configuring default CookieSerializer using SessionCookieConfig
Fixes gh-87
2017-06-13 22:01:15 +02:00
Rob Winch
7b385c7d33 Exclude check on artifactoryPublish 2017-06-08 16:54:16 -05:00
Rob Winch
87d51c54c9 Add Artifactory Deploy 2017-06-08 16:42:55 -05:00
Rob Winch
210e8eebc5 Update to spring-build-conventions 0.0.2.BUILD-SNAPSHOT 2017-06-08 16:38:39 -05:00
Vedran Pavic
7d52c87173 Improve Spring Boot based samples
Starting with 2.0.0.M1, Boot provides auto-config support for Lettuce.

See gh-790
2017-06-07 22:48:46 +02:00
Vedran Pavic
e3c6fb67f2 Replace StringBuffer usages with StringBuilder
Fixes gh-718
2017-05-30 22:40:28 +02:00
Vedran Pavic
79f187ddd6 Upgrade samples to Boot 2.0.0.M1
Fixes gh-782
2017-05-30 22:37:36 +02:00
Vedran Pavic
22f4b0bc9d Update minimum requirements 2017-05-25 23:59:49 +02:00
Vedran Pavic
c5ea626d03 Improve layout of community extensions doc section
Fixes gh-786
2017-05-17 11:49:49 +02:00
Vedran Pavic
d067cd1e66 Remove obsolete .gradle files
Fixes gh-785
2017-05-17 11:45:56 +02:00
Rob Winch
76a6be572a Next development version 2017-05-10 13:47:17 -05:00
Spring Buildmaster
78db900303 Release version 2.0.0.M2 2017-05-10 07:08:41 +00:00
Rob Winch
7f9a9c4185 Update to lettuce-core 5.0.0.M2 2017-05-10 01:44:44 -05:00
Rob Winch
83c67d3e11 Update to Spring Security 5.0.0.M1 2017-05-10 01:44:18 -05:00
Rob Winch
4f3324bac4 Update to spring-build-conventions 0.0.1.RELEASE 2017-05-10 01:44:04 -05:00
Rob Winch
23a28f790a Disable OSSRH Deploy
For the release disable OSSRH Deploy since we will release only
to Artifactory for the milestone. We cannot deploy to OSSRH because
we have RC dependencies that are not in Maven Central.
2017-05-10 00:46:45 -05:00
Vedran Pavic
dd4983f33e Improve samples logging 2017-05-10 00:24:57 -05:00
Vedran Pavic
e9e5d8eda6 Parameterize SpringSessionBackedSessionRegistry 2017-05-10 00:24:16 -05:00
Rob Winch
a745d471ad Update to Spring Data Kay M3 2017-05-09 09:36:54 -05:00
Rob Winch
df267774da Use dependency-management.gradle 2017-05-09 02:52:53 -05:00
Vedran Pavic
2b2f385d5f Use spring-jcl instead of commons-logging 2017-05-09 02:50:27 -05:00
Vedran Pavic
86e892c806 Improve Spring Boot based samples
Fixes gh-693
2017-05-04 18:56:12 +02:00
Vedran Pavic
448133494f Replace anonymous types with lambda 2017-05-03 10:06:56 -05:00
Vedran Pavic
e0fc9e92ba Simplify Map.get and conditional calls 2017-05-03 10:06:56 -05:00
Vedran Pavic
5b4d0c40d8 Replace explicit type arguments with diamond operator 2017-05-03 10:06:56 -05:00
Rob Winch
78ea101a43 Add logback.xml
This will ensure the logs are not too large which will improve performance
and ensure that the build works on Travis which caps the log file size.
2017-05-03 10:06:44 -05:00
Vedran Pavic
63097e9d82 Fix broken documentation links 2017-04-28 20:05:37 +02:00
Vedran Pavic
2ebbe762f0 Downgrade Spring Boot to 1.5.3.RELEASE
Fixes gh-710
2017-04-28 17:27:49 +02:00
Rob Winch
63b836b212 Remove Mongo
Fixes gh-768
2017-04-26 22:19:10 -05:00
Rob Winch
02da23a2a0 Remove GemFire
Fixes gh-768
2017-04-26 22:19:10 -05:00
Rob Winch
b254c7c6b9 Add Jenkinsfile
Fixes gh-770
2017-04-26 22:19:10 -05:00
Rob Winch
89adc13201 Use Optional 2017-04-26 22:19:10 -05:00
Rob Winch
e6e752aea5 Servlet 3.1 compatible 2017-04-26 22:19:10 -05:00
Rob Winch
d590ca58e4 Standardize Build
Fixes gh-769
2017-04-26 22:11:41 -05:00
Rob Winch
e23aaeca5f Fix .gitignore 2017-04-26 22:11:41 -05:00
Vedran Pavic
0312c31a42 Use explicit constraints in JDBC schema scripts
Fixes gh-750
2017-04-26 23:31:18 +02:00
Vedran Pavic
815cbf4ee8 Update DefaultCookieSerializer to use base64 by default
Fixes gh-736
2017-04-26 22:49:47 +02:00
Eddú Meléndez
6327d36ce9 Use Base64 implementation provided by Java 8
Fixes gh-735
2017-04-26 22:43:44 +02:00
Rob Winch
707b8bb062 Revert "Workaround Lettuce Bug"
This reverts commit adbff45a23.

Fixes gh-759
2017-04-26 11:30:28 -05:00
Rob Winch
adbff45a23 Workaround Lettuce Bug
Issue gh-759
2017-04-26 08:57:22 -05:00
Rob Winch
f30cb7a1e6 SpringSessionRememberMeServices rm SecurityContext attribute
SpringSessionRememberMeServices use to invalidate the session which would
cause Spring Security's saved request to be lost.

Now SpringSessionRememberMeServices deletes the SecurityContext from the
HttpSession instead.

Fixes gh-752
2017-04-26 08:57:22 -05:00
John Blum
432eb84a94 Restore proper behavior of HttpSession created events in GemFire support when client Region is a PROXY in the client/server topology
Fixes gh-757
2017-04-25 20:03:11 -07:00
John Blum
bd31710117 Upgrade to Jackson 2.9.0.pr2
Jackson 2.9.0.pr2 is required by the latest Spring Data Redis Kay
2017-04-22 01:20:46 -07:00
John Blum
e67afefcd8 Fix Java 8 ambiguous compiler errors 2017-04-22 01:04:19 -07:00
John Blum
327323da38 Upgrade to io.spring.dependency-management Gradle plugin 1.0.2.RELEASE
Apply io.spring.dependency-management Gradle plugin to all Boot samples to properly resolve Boot dependencies with implicit versioning
2017-04-21 23:21:39 -07:00
John Blum
83e5d6f2a7 Upgrade to Java 8 source and target baseline 2017-04-21 23:19:01 -07:00
John Blum
887f024551 Fix test failure
Polish for #gh-755
2017-04-21 23:16:45 -07:00
John Blum
8dd6aa38ed Upgrade to io.lettuce:lettuce-core:5.0.0.BUILD-SNAPSHOT 2017-04-21 22:16:05 -07:00
John Blum
6f4025eacb Simply GemFire configuration in docs GemFire Indexing integration tests 2017-04-21 22:04:50 -07:00
John Blum
3cc53fae2c Upgrade to Gradle 3.5 2017-04-21 21:00:24 -07:00
John Blum
25ded686ac Improve GemFire SessionRepository, Session copy logic to avoid issues with delta propagation on updates
Fixes #gh-755

(cherry picked from commit dcc0c07981)
Signed-off-by: John Blum <jblum@pivotal.io>
2017-04-21 20:58:44 -07:00
Vedran Pavic
9b30726805 Fix typo in .gitignore 2017-03-23 19:14:16 +01:00
Vedran Pavic
aeb182712c Remove logging for "Skip invoking on" response committed
Fixes gh-734
2017-03-20 20:19:49 +01:00
Sebastian Laskawiec
18ccee051f Add link to Infinispan for Spring Session documentation
Fixes gh-745
2017-03-20 19:53:13 +01:00
Vedran Pavic
3f239b4956 Fix Spring Boot deprecation warnings 2017-03-05 12:13:11 +01:00
Vedran Pavic
dc3b6ba6f1 Fix Mockito deprecation warnings 2017-03-05 12:13:03 +01:00
Rob Winch
ddf9ef66c1 Fix selenium versions
Spring Platform Updated versions. Fix selenium dependencies to work
with them.
2017-03-03 10:58:27 -06:00
Rob Winch
b65423f296 FindByUsernameTests check if driver is null
Fixes gh-740
2017-03-03 10:53:12 -06:00
John Blum
b3706addbb Introduce more reliable coordination between a GemFire client/server during integration tests.
Fixes gh-672
2017-01-31 12:47:28 -08:00
John Blum
5c6565bd9c Fix compilation error caused by improper use of Assert.notNull(..)
Fixes gh-724
2017-01-30 16:10:34 -08:00
John Blum
3e24393e9a Fixes GemFire client/server integratione tests issue when setting JAVA_TOOL_OPTIONS env var
Fixes gh-669
2017-01-30 15:20:06 -08:00
John Blum
0e10b7763c Remove 'thymeleaf-extras-conditionalcomments' dependency
Fixes gh-721
2017-01-25 22:27:51 -08:00
Vedran Pavic
012f121c48 Prevent NPE inMongoOperationsSessionRepository when creating session if max inactive interval is undefined
Fixes gh-716
2017-01-21 22:58:06 +01:00
Vedran Pavic
c0cc15679c Improve Hazelcast support documentation
Fixes gh-680
2017-01-17 21:36:40 +01:00
Rob Winch
43d83f6398 Polish 2017-01-16 16:05:36 -06:00
Vedran Pavic
862659b9b7 Restructure samples 2017-01-14 10:29:30 +01:00
John Blum
536156a4ec Consistentely apply Spring Data BOM across Spring Session modules
Fixes gh-709
2017-01-13 17:54:44 -08:00
John Blum
aa3536a71a Set Spring dependencies to build snapshots
Sets Spring Framework to 5.0.0.BUILD-SNAPSHOT

Sets Spring Data to 2.0.0.BUILD-SNAPSHOT

Sets Spring Data Release Train to Kay-BUILD-SNAPSHOT

Sets Spring Security to 4.2.2.BUILD-SNAPSHOT

Fixes gh-709
2017-01-13 16:05:12 -08:00
John Blum
dd23c96c1a Set Spring Boot version to 2.0.0.BUILD-SNAPSHOT
Fixes gh-709
2017-01-13 16:01:50 -08:00
Vedran Pavic
41fbc90ec2 Fix Gradle deprecation warnings 2017-01-12 21:58:03 +01:00
Vedran Pavic
3cc3784313 Remove deprecations
This commit removes `SessionMessageListener` and `CookieHttpSessionStrategy#setCookieName` which both were deprecated since `1.1.0`, and `SessionEntryListener` which was deprecated since `1.3.0`.

Fixes gh-675
2017-01-12 21:20:52 +01:00
Eddú Meléndez
489cf01812 Polish samples
Fixes gh-698
2017-01-12 21:15:50 +01:00
Vedran Pavic
94fc80a8f0 Use capitalized words for HeaderHttpSessionStrategy default header name
Fixes gh-173
2017-01-12 18:54:53 +01:00
Rob Winch
3ad0028785 Use relative xpath
Issue gh-702
2017-01-11 15:54:03 -06:00
Rob Winch
801f88d793 Remove lombok from build.gradle
Issue gh-702
2017-01-11 15:40:02 -06:00
Rob Winch
0d6b62b7a9 Remove Lombok from httpsession-gemfire-boot
Fixes gh-702
2017-01-11 15:29:21 -06:00
Rob Winch
00d5d76833 Fix Buildship import with eclipse.jdt.javaRuntimeName
See https://discuss.gradle.org/t/building-with-newer-jdks/21102
2017-01-11 14:38:31 -06:00
Rob Winch
85a1b43242 Update to Gradle 3.3 2017-01-11 14:38:14 -06:00
John Blum
800d52279f Disable spring3Test check 2017-01-10 02:02:59 -08:00
John Blum
61a6344ffa Upgrade to Spring Data Kay
Dependency updates supporting Kay:

Upgrade Spring Framework to 5.0.0.M3

Upgrade Spring Boot to 1.5.0.RC1

Upgrade Jackson to 2.7.6

Upgrade Jedis to 2.9.0

Upgrade Lettuce to 5.0.0.Beta1

Upgrade Mockito to 2.5.4

Fixes #gh-677
2017-01-10 01:52:21 -08:00
Rob Winch
c182e90a1a httpsession-xml fixes 2017-01-09 21:49:48 -08:00
Rob Winch
7dc3e12e07 Polish httpsession-redis-json 2017-01-09 21:49:38 -08:00
Rob Winch
ce5e44233e Polish httpsession-jdbc-xml 2017-01-09 21:49:30 -08:00
Rob Winch
22c416e32b Polish jdbc-boot 2017-01-09 21:49:23 -08:00
Rob Winch
ff72bf1234 Additional boot polish 2017-01-09 21:49:14 -08:00
Rob Winch
a45199059b Polish httpsession-jdbc 2017-01-09 21:49:08 -08:00
Rob Winch
37045e337c Polish httpsession 2017-01-09 21:49:00 -08:00
Rob Winch
41e3f91b75 Polish hazelcast-spring 2017-01-09 21:48:50 -08:00
Rob Winch
efeed5e2cf Polish hazelcast 2017-01-09 21:48:42 -08:00
Rob Winch
1952a4550f Polish findbyusername 2017-01-09 21:48:34 -08:00
Rob Winch
9efb5b59e5 Polish custom-cookies 2017-01-09 21:48:23 -08:00
Rob Winch
4b196744f2 Polish 2017-01-09 21:48:15 -08:00
Pool Dolorier
8e7c736a0a Move groovy test to java 2017-01-09 21:48:02 -08:00
Eddú Meléndez
1a318b89d9 Convert groovy tests to java 2017-01-09 21:47:57 -08:00
Rob Winch
f98697416e spring-boot-starter-data-redis 2017-01-09 21:47:47 -08:00
Rob Winch
d66fa56513 Spring IO defaults to Cairo 2017-01-09 21:47:41 -08:00
Rob Winch
89c91c19d8 Fix spring platform Brussels
* Update to dependency-management-1.0.0.RC2
* Update to spring-io-plugin 0.0.6.RELEASE
* Update to Boot 1.5 (which requires newer dependency management plugin)
2017-01-05 14:59:15 -06:00
Rob Winch
5e294b805f Fix Formatter 2017-01-05 14:05:07 -06:00
Rob Winch
a7b5f86bcd Start 2.0.0.BUILD-SNAPSHOT 2016-12-19 13:39:02 -06:00
Rob Winch
9c236fa256 Start 1.4.x 2016-12-19 13:38:08 -06:00
Rob Winch
304e32eef5 Fix Typo 2016-12-19 13:35:07 -06:00
Spring Buildmaster
288c622012 Next development version 2016-12-15 22:30:26 +00:00
Spring Buildmaster
3827ae1e72 Release version 1.3.0.RELEASE 2016-12-15 22:30:19 +00:00
Rob Winch
9067f8235d Add What's New in 1.3
Fixes gh-627
2016-12-15 15:56:16 -06:00
Rob Winch
19928e6b7f Add MongoSession.isExpired interval < 0 Test
Issue gh-629
2016-12-14 08:52:43 -06:00
Joe Atkins
1df1a76069 Prevent expiration on RedisSession interval < 0
Since a negative maxInactiveInterval is supposed to disable
expiration, if it is negative, use persist on the session's
spring:session:session and spring:session:expires keys to
prevent the expiration of the RedisSession.

Issue gh-629
2016-12-14 08:51:19 -06:00
Joe Atkins
17e397212d Fix MongoExpiringSession.isExpired interval < 0
Verify that interval is non-negative, as negative values for
maxInactiveInterval disables expiration.

Issue gh-629
2016-12-14 08:49:26 -06:00
Vedran Pavic
39503a21a7 Refactor JdbcOperationsSessionRepository session clean up query to prevent overflow
Fixes gh-679
2016-12-02 21:29:47 +01:00
Gabor Csizmadia
ebbc10b2b4 Fix misleading comment about SessionCreatedEvent
Fixes gh-678
2016-11-28 22:54:06 +01:00
John Blum
9a51cb9ca7 Minor changes to improve the timing between Spring Boot-based GemFire client and server connections
Fixes gh-672
2016-11-22 22:48:37 -08:00
Spring Buildmaster
5e0ee5077a Next development version 2016-11-23 04:15:45 +00:00
Spring Buildmaster
526c6ee012 Release version 1.3.0.RC1 2016-11-23 04:15:39 +00:00
Eddú Meléndez
ff4045acbd Support placeholder resolution for collectionName in EnableMongoHttpSession 2016-11-22 21:48:54 -06:00
Eddú Meléndez
2d359986d3 Support placeholder resolution for redisNamespace in EnableRedisHttpSession annotation
Fixes gh-381
2016-11-22 21:45:55 -06:00
Eddú Meléndez
6424910c83 Support placeholder resolution for tableName in EnableJdbcHttpSession
Fixes gh-512
2016-11-22 21:22:19 -06:00
Rob Winch
49e3a1c7cd Polish SpringSessionRememberMeServices
* Move to ~.security.web.authentication package
* Add documentation
* Use getBeanNamesForType to avoid eager bean initialization
* Remove rememberMeCookieMaxAge because it must be Integer.MAX_VALUE since
  cookie is only written at the creation of the session
* Change from parameter to rememberMeParameterName

Issue gh-189
2016-11-22 20:46:42 -06:00
Rob Winch
3b8258f233 Polish Checkstyle 2016-11-22 16:20:42 -06:00
Vedran Pavic
a2b30eb54b Add Spring Security's RememberMeServices implementation backed by Spring Session 2016-11-19 15:11:04 +01:00
marcoblos
4c2581d432 Polish RedisSession
Maintain standard using  prefix inside the RedisSession to mathods
call. Generalizing  and  methods.

Fixes gh-638
2016-11-17 10:33:18 -06:00
Rob Winch
25aec99357 Polish CURRENT_SESSION_ATTR
* Remove unnecessary comment
* Make relative to SESSION_REPOSITORY_ATTR

Issue gh-654
2016-11-16 14:55:10 -06:00
Alex Panchenko
1c9dfa6638 static field for constant in SessionRepositoryFilter
Fixes gh-654
2016-11-16 14:54:57 -06:00
Rob Winch
466e2cf102 Fix docs security-config.xml 2016-11-16 14:31:45 -06:00
Vedran Pavic
eb0f292c20 Fix docs module Sonar build 2016-11-16 17:59:36 +01:00
Vedran Pavic
43fda301e2 Fix Sonar build 2016-11-15 23:07:00 +01:00
Vedran Pavic
4a06b38c5f Polish contribution (#516)
Issue gh-516
2016-11-15 21:20:22 +01:00
Aleksandar Stojsavljevic
6a78101db5 Optimize save operation in HazelcastSessionRepository (#516)
This commit improves saving of sessions to only execute save
operation if something has been changed
(e.g. session.setAttribute(String, Object) was called).
Further, configurable flush mode that specifies when to write
to the backing Hazelcast instance is introduced. It can be
'on save' (default) or 'immediate'.

Fixes gh-516, fixes gh-641
2016-11-15 21:16:33 +01:00
Rob Winch
b5ea6c752d Update to Sonar 2.2 to work with Gradle 3.1 2016-11-14 18:45:56 -06:00
Rob Winch
46633274d5 Add eclipseConfiguration task 2016-11-14 16:54:16 -06:00
Rob Winch
038287b2cc Use $mockitoVersion 2016-11-14 16:54:16 -06:00
Rob Winch
32c053271c Add Tests for Closing RedisConnection
Issue gh-656
2016-11-14 16:54:16 -06:00
Alex Panchenko
802e0e714b close RedisConnection in EnableRedisKeyspaceNotificationsInitializer
Issue gh-626
2016-11-14 13:49:41 -06:00
Vedran Pavic
6263f6e927 Upgrade Gradle to 3.1
Fixes gh-668
2016-11-14 18:48:25 +01:00
Vedran Pavic
5671037c39 Delete refactor from findbyusername doc
Fixes gh-671
2016-11-14 18:33:49 +01:00
Vedran Pavic
3d44467275 Fix broken link in find by username guide
Fixes gh-670
2016-11-14 18:17:10 +01:00
Eddú Meléndez
94221c70a9 Clear warnings from spring-boot gradle plugin 2016-11-11 15:27:55 -06:00
John Blum
dd3a571494 Avoid premature destruction of the GemFire Pool used by the client Sessions Region.
Fixes gh-665
2016-11-10 18:09:39 -08:00
Mark Paluch
e4fe53abf8 Update Samples and Guides to use Lettuce
Favor lettuce because of multiplexing and improved scalability.
Using lettuce requires a fixed number of connections hence using
lettuce improves application scalability.

Fixes gh-652
2016-11-10 16:56:19 -06:00
Rob Winch
7b65d7930b Fix Checkstyle
Issue gh-657
2016-11-10 16:30:59 -06:00
Rob Winch
0f8326516b Add Tests
Issue gh-657
2016-11-10 16:13:18 -06:00
Alex Panchenko
2aec28289e Skip redis getConnection() if ConfigureRedisAction.NO_OP
Issue: gh-653
2016-11-10 16:01:04 -06:00
Rob Winch
af7a5a208f Update to Spring Boot 1.4.2.RELEASE
Fixes gh-664
2016-11-10 15:38:38 -06:00
Rob Winch
e9924d27a1 Update to Spring 4.3.4.RELEASE
Fixes gh-663
2016-11-10 15:38:22 -06:00
John Blum
f0820c8038 Update to Spring Boot 1.4.1.RELEASE
* Upgrade to Spring Data GemFire 1.8.4.RELEASE
 * Upgrade to Spring Data MongoDB 1.9.4.RELEASE
 * Upgrade to Spring Framework 4.3.3.RELEASE

(Upgrade to Spring Data Redis 1.7.4.RELEASE failed)

Fixes gh-632
2016-11-10 15:37:25 -06:00
Rob Winch
7d680ff3ef Update to Spring Security 4.2.0
Fixes gh-662
2016-11-10 15:37:17 -06:00
Rob Winch
6d9885455b Explicit Spring IO Version 2016-10-13 08:12:24 -05:00
Rob Winch
06104c348d Revert "Add Apache Geode support (#366)"
This revert is done because Geode is not supported by Spring IO yet.

This reverts commit 1256a94d7e.

# Conflicts:
#	gradle.properties
#	settings.gradle
2016-10-13 08:12:24 -05:00
Vedran Pavic
090742350c Polish RedisOperationsSessionRepository 2016-09-30 14:17:52 -05:00
Rob Winch
a290b11019 Add OrientDB to What's New 2016-09-30 10:09:30 -05:00
Miron Aseev
eabad84ba8 Add a link to Spring Session OrientDB project 2016-09-30 10:07:44 -05:00
Rob Winch
4e33b7740c Update to Spring Security 4.2.0.M1 2016-09-23 15:56:06 -05:00
Spring Buildmaster
f8967c4c13 Next development version 2016-09-14 19:08:20 +00:00
Spring Buildmaster
245e634bea Release version 1.3.0.M2 2016-09-14 19:08:14 +00:00
Rob Winch
7a2914323f Update to Hazelcast 3.6.5
Issue gh-544
2016-09-14 11:32:03 -05:00
Vedran Pavic
6e04d903ae Add HazelcastSessionRepository
This commit improves existing Hazelcast support, which is based on
MapSessionRepository, with dedicated HazelcastSessionRepository
that implements the FindByIndexNameSessionRepository contract.

Also a new hazelcast-spring-session module was added to provide
dependency management for Hazelcast support.

Fixes gh-544
2016-09-14 11:22:56 -05:00
Spring Buildmaster
d8c3a4dd61 Next development version 2016-09-13 21:21:21 +00:00
Spring Buildmaster
997813088a Release version 1.3.0.M1 2016-09-13 21:21:14 +00:00
Rob Winch
5ecf390932 Add spring.session.cleanup.cron.expression
Fixes gh-616
2016-09-13 10:41:31 -05:00
Rob Winch
8167b43e63 Update to AssertJ 2.5.0 2016-09-13 10:29:14 -05:00
Rob Winch
1f7193f32d Polish
Issue gh-434
2016-09-12 16:02:45 -05:00
Rob Winch
0e1d81f509 Add Spring Security jackson2
Issue gh-434
2016-09-12 16:02:40 -05:00
Jitendra Singh Bisht
8b97a32db2 Mix-ins added for Jackson Serialization/deserialization
Fixes gh-434
2016-09-12 16:02:21 -05:00
Vedran Pavic
d3379029bb Fix custom expire for JdbcOperationsSessionRepository.cleanUpExpiredSessions
Currently, JdbcOperationsSessionRepository#cleanUpExpiredSessions only considers
the repository defined max inactive interval which causes incorrect cleanup of
sessions that define custom inactive interval. This commit fixes the problem by
delegating calculation of deletion interval to the underlying SQL DELETE statement.

Fixes gh-580
2016-09-12 13:09:36 -05:00
Vedran Pavic
26eca5b448 Add custom SQL JdbcOperationsSessionRepository
This commit allows custom SQL queries for
JdbcOperationsSessionRepository.

Fixes gh-609
2016-09-12 13:08:15 -05:00
Vedran Pavić
6335894e13 Restore Unix (LF) line endings in CookieHttpSessionStrategyTests (#625) 2016-09-12 12:54:12 -05:00
Vedran Pavic
34948d6451 Improve JDBC integration tests 2016-09-12 11:28:05 -05:00
Eric Deandrea
bff0f8f845 Fix CookieHttpSessionStrategy encodeUrl
Fix to https://support.pivotal.io/tickets/30469

I do notice 2 bugs in the spring-session code, both inside the
CookieHttpSessionStrategy class.

The first, using their own sample application, after getting a new
session alias from the HttpSessionManager, it calls
HttpSessionManager.encodeURL and then stores the output into a request
attribute which is later rendered. However, in a Spring MVC
application, a Controller may want to issue a redirect to a
newly-encoded URL, like this:

HttpSessionManager sessionManager = (HttpSessionManager)
request.getAttribute(HttpSessionManager.class.getName());
String newSessionAlias = sessionManager.getNewSessionAlias(request);
String currentSessionAlias =
sessionManager.getCurrentSessionAlias(request);

return String.format("redirect:%s",
sessionManager.encodeURL("/effectiveUser", newSessionAlias));

The problem here is that Spring MVC will funnel the redirect back
through
CookieHttpSessionStrategy.MultiSessionHttpServletResponse.encodeRedirect
URL, which then looks up the current alias & re-encodes with that
alias, effectively replacing the new alias which the controller added.
The fix would be for the
CookieHttpSessionStrategy.MultiSessionHttpServletResponse to examine
the inputted url for the appropriate parameter & don't re-encode if it
finds it there.

The second issue is that inside CookieHttpSessionStrategy line 346
(return path + "?" + query;) is that if there is no query string (on
the default session alias) then the resulting URL will end in a ? with
nothing after it. In that situation the ? should be removed as well
because the query string is empty.
2016-09-12 11:03:57 -05:00
Rob Winch
2052ec8d44 Polish JDBC Bean ClassLoader
Issue gh-610
2016-09-07 11:08:59 -05:00
Vedran Pavic
cbd96999e0 JDBC uses Bean ClassLoader
This commit addresses the issue with deserializing JDBC sessions in Spring Boot
applications that use DevTools. Previously, such configuration would cause
`ClassCastException` when deserializing JDBC sessions due to app class loader
being used instead of restart class loader.

Fixes gh-610
2016-09-07 11:08:58 -05:00
Rob Winch
61492c4ae1 Add ability to set delimiters for CookieHttpSessionStrategy
Fixes gh-615
2016-09-06 23:20:53 -05:00
Rob Winch
97fef0f9bd Polish Base64 DefaultCookieSerializer Support
Issue gh-611
2016-09-06 21:20:20 -05:00
Vedran Pavic
2792d2a0e9 Add DefaultCookieSerializer Base64 Support
Fixes gh-611
2016-09-06 21:19:52 -05:00
Joris Kuipers
2724b333b3 Spring security session registry (#473)
*  Spring Security Concurrent Session Integration #65

 add SpringSessionBackedSessionRegistry

*  Spring Security Concurrent Session Integration #65

 add documentation

*  Spring Security Concurrent Session Integration #65

support marking SessionInformations as expired before deleting the Session
2016-09-02 14:01:41 -05:00
Vedran Pavić
8db7d394ba Fix broken samples (#606) 2016-09-02 13:44:17 -05:00
Vedran Pavić
3e293e8b54 Polish JDBC configuration (#608) 2016-09-02 13:42:36 -05:00
Rob Winch
de7bb05fc1 Remove only master from .travis.yml 2016-08-30 09:16:34 -05:00
John Blum
3ee4c5b5d0 Register non-anonymous, named Instantiators for GemFireSession and GemFireSessionAttributes (#594)
Fixes gh-594 & gh-595
2016-08-17 17:35:00 -07:00
John Blum
acf37fc8f4 Fix deserialization issue caused by unregistered Instantiator (#595)
Fixes gh-594 & gh-595
2016-08-17 09:22:50 -07:00
John Blum
1256a94d7e Add Apache Geode support (#366)
Closes gh-365, gh-366
2016-08-15 14:53:12 -05:00
Vedran Pavić
bbb94361f8 Optimize save operation in JdbcOperationsSessionRepository (#582)
This commit improves saving of new sessions to only execute batch update operation if there really are any attributes to save.
2016-08-15 14:25:37 -05:00
John Blum
9af3f1fcec Enable named Pool configuration on cache client 'Session' Region (#487)
Fixes gh-484
2016-08-15 14:14:34 -05:00
Vedran Pavić
5b70d55a21 Improve result set extraction in JdbcOperationsSessionRepository (#577)
Previously, result set extraction was performed by a `RowMapper` implementation that required scrollable result sets support which caused compatibility issues with some database vendors.

This commit improves result set extraction by using a `ResultSetExtractor` which does not require scrollable result sets.
2016-08-15 13:34:12 -05:00
Vedran Pavić
9133e337e6 Use webjars-locator in samples (#587) 2016-08-15 13:25:53 -05:00
Rob Winch
9f36fd69ee Version is 1.3.0.BUILD-SNAPSHOT 2016-06-29 22:52:40 -05:00
Spring Buildmaster
e684f58403 Next development version 2016-06-30 03:05:34 +00:00
Spring Buildmaster
508e3e90a7 Release version 1.2.1.RELEASE 2016-06-30 03:05:19 +00:00
Vedran Pavic
0018a2f772 Add database schema drop scripts
Fixes gh-565
2016-06-29 21:21:53 -05:00
Rob Winch
e9f097db5c Gemfire Boot Sample uses Random Port
Previously, the Gemfire Boot Sample didn't select a random port for
Tomcat to run on.

Issue gh-496
2016-06-29 21:02:10 -05:00
Rob Winch
7e41e40762 Fix Gemfire ClientConfig BeanFactoryPostProcessor
@Bean method ClientConfig.propertySourcesPlaceholderConfigurer was
non-static and returns an object assignable to Spring's
BeanFactoryPostProcessor interface. This will result in a failure to
process annotations such as @Autowired, @Resource and @PostConstruct
within the method's declaring @Configuration class.

This commit makes the method static to avoid the problem.
2016-06-29 21:02:10 -05:00
Rob Winch
dc553104c6 Fix SessionRepositoryFilterTests doFilterLastAccessedTime assertion
Relax the assertion so that it is not exact to avoid timing issues.

Fixes gh-519
2016-06-29 16:24:52 -05:00
Vedran Pavic
2b4a349b1a Use webjars for static resources in sample apps 2016-06-29 12:59:54 -05:00
Rob Winch
4a1f132e07 Add JdbcOperationsSessionRepository overflow test
Add test for ensuring
JdbcOperationsSessionRepository.cleanupExpiredSessions does not have an
overflow when setting the max inactive interval to large values.

Issue gh-564
2016-06-29 11:58:18 -05:00
pasali
940555653c Fix overflow in JdbcOperationsSessionRepository
Changed maxInactiveIntervalSeconds to long in order to prevent integer
overflow in cleanUpExpiredSessions method.

Fixes gh-564
2016-06-29 11:56:03 -05:00
Rob Winch
82cb417910 Boot Gemfire Sample JDK7 compatible
Issue gh-496
2016-06-29 11:22:16 -05:00
Rob Winch
808a550343 Polish Boot Gemfire Sample
Issue gh-496
2016-06-29 11:08:05 -05:00
Rob Winch
cc08f83ce0 Fix Gemfire Eclipse & Checkstyle 2016-06-29 11:07:58 -05:00
John Blum
53c81de5e3 Add Spring Session Data GemFire with Spring Boot sample
Fixes gh-496
2016-06-29 10:34:58 -05:00
Vedran Pavić
dd268992a8 Update GitHub Pull Request template
Remove the CLA checkbox

Fixes gh-558
2016-06-21 14:04:39 -05:00
Eddú Meléndez
00004e2f50 Polish build.gradle samples
Disable publishing samples artifacts.

Fixes gh-518
2016-06-19 03:01:35 +10:00
Rob Winch
afb9a11abf Update to CLA tooling 2016-06-08 21:58:20 -05:00
Vedran Pavić
fb356f02ff SessionRepositoryFilterTests Sleeps for 50 ms 2016-06-06 08:45:19 -05:00
Vedran Pavić
509fb99a5d Improve JDBC transaction management doc 2016-05-20 11:56:58 -05:00
Vedran Pavić
2aff5b0377 Fix EnableRedisHttpSession.redisFlushMode javadoc
Fix EnableRedisHttpSession.redisFlushMode javadoc
2016-05-16 08:04:47 -05:00
Spring Buildmaster
016a007d12 Next development version 2016-05-12 01:28:55 +00:00
Spring Buildmaster
2a3ec2f4b9 Release version 1.2.0.RELEASE 2016-05-12 01:28:40 +00:00
Vedran Pavić
458ac6e700 Increase default size for attribute_name column (#515) 2016-05-06 17:03:40 -05:00
Vedran Pavić
33321d2e53 Polish MongoHttpSessionConfigurationTests
This improves test coverage and makes the MongoHttpSessionConfigurationTests
more consistent with analogous tests for other repositories.

Fixes gh-503
2016-05-02 15:11:54 -05:00
Spring Buildmaster
20130f8e8a Next development version 2016-04-27 01:19:07 +00:00
Spring Buildmaster
03f008f283 Release version 1.2.0.RC3 2016-04-27 01:18:46 +00:00
Vedran Pavic
00e7110594 Implement individual attribute persistence in JdbcOperationsSessionRepository 2016-04-26 14:29:23 -05:00
Eddú Meléndez Gonzales
f973e63fce Set collectionName attribute in MongoOperationsSessionRepository
Previous to this commit, collectionName could be set in
MongoHttpSessionConfiguration but it was never used. Now, attribute
can be set into MongoOperationsSessionRepository to take effect.

See gh-489
2016-04-26 14:27:02 -05:00
Eddú Meléndez
edbf8bf587 Expose attributes in MongoHttpSessionConfiguration
Attributes collectionName and maxInactiveIntervalInSeconds can be set
now.

Fixes gh-489
2016-04-20 13:49:18 -05:00
Johnny Lim
234f6c954a Remove duplicate words
Remove duplicate words

Fixes gh-494
2016-04-18 23:27:48 -05:00
Rob Winch
e0417523f6 Add tableName setter
Add tableName setter for JdbcHttpSessionConfiguration
2016-04-18 23:18:58 -05:00
Eddú Meléndez
5865b0a715 Add tableName setter for JdbcHttpSessionConfiguration
See gh-488
2016-04-19 01:00:40 +10:00
Vedran Pavić
67201417df Add compile dependency to commons-logging (#476) 2016-04-11 10:55:10 -05:00
Vedran Pavić
01a149737e Add GitHub issue/PR tempaltes
Add
2016-04-11 10:54:13 -05:00
John Blum
2d6f505a30 Update to Spring Data Hopper
Fixes gh-470
2016-04-11 09:31:05 -05:00
Rob Winch
7c616a1adf Redis save does nothing if nothing has changed
Fixes gh-467
2016-04-06 15:38:34 -05:00
Spring Buildmaster
61b01d9ecd Next development version 2016-04-06 14:26:42 +00:00
Spring Buildmaster
f9163d94fd Release version 1.2.0.RC2 2016-04-06 14:26:36 +00:00
Vedran Pavić
3f819a94b1 Enable transaction management for JdbcOperationsSessionRepository operations 2016-04-05 23:39:36 -05:00
Rob Winch
1e1d24895c Merge pull request #454 from vpavic/jdbc-boot-sample
Add JDBC Spring Boot sample
2016-04-04 08:36:27 -05:00
Rob Winch
e134a4cdb8 Merge pull request #463 from vpavic/fix-h2-console-urls
Make H2 console URL consistent across sample projects
2016-04-04 07:35:45 -05:00
Rob Winch
75006cd7dd Merge pull request #462 from vpavic/gh-457
Update WebSocket sample to use H2 console auto-configuration
2016-04-04 07:34:09 -05:00
Vedran Pavic
9c8f8894e1 Add JDBC Spring Boot sample 2016-04-03 20:57:16 +02:00
Vedran Pavic
99db45ea72 Make H2 console URL consistent across sample projects 2016-04-01 22:36:24 +02:00
Vedran Pavic
c07583bd47 Update WebSocket sample to use H2 console auto-configuration
Fixes gh-457.
2016-04-01 22:26:13 +02:00
Rob Winch
cce8dac4b7 Fix WebSocket AbstractMethodError
Fixes gh-460
2016-04-01 14:27:08 -05:00
Rob Winch
5bde226ecc Fix Eclipse compile errors
* Most web.xml servlet API versions updated to 3.0 for ASYNC support
* httpsession-xml is left at 2.5 to ensure compatability & remove ASYNC
* Remove @Override on interface override
2016-04-01 11:56:27 -05:00
Rob Winch
f8f6ee20c0 Externalize sample.gradle 2016-03-30 10:23:11 -05:00
Rob Winch
3bb96e8e82 Remove Wrapper from Gradle Sample
Issue gh-246
2016-03-30 10:22:59 -05:00
Rob Winch
b7367680cb Fix Package Names in Grails Sample
Issue gh-246
2016-03-30 10:22:59 -05:00
Rob Winch
a26a21b663 Add Grails 3 Sample to What's New
Issue gh-246
2016-03-30 09:28:03 -05:00
Rob Winch
3825a46418 Polish Grails 3 Sample
Issue gh-246
2016-03-29 20:58:26 -05:00
Eric Helgeson
779277b16d Add Grails Sample
Fixes gh-246
2016-03-29 20:58:26 -05:00
Rob Winch
b97306a83d Merge pull request #450 from vpavic/gh-445
Fix loading of JdbcSession's lastAccessedTime attribute
2016-03-28 15:06:49 -05:00
Rob Winch
6b13111079 Merge pull request #451 from vpavic/polish-tests
Polish JdbcHttpSessionConfigurationTests
2016-03-28 11:15:15 -05:00
Vedran Pavic
63006db45d Fix loading of JdbcSession's lastAccessedTime attribute
Fixes gh-445.
2016-03-28 18:13:16 +02:00
Vedran Pavic
0a99e065ff Polish JdbcHttpSessionConfigurationTests 2016-03-28 17:49:02 +02:00
Scott Carlson
bd2d846917 Add Dispatcher types to web.xml
Fixes gh-443
2016-03-28 09:16:13 -05:00
Rob Winch
79928bd7fe Merge pull request #436 from vpavic/improve-mongo-it
Use Flapdoodle Embedded MongoDB for integration tests
2016-03-25 09:30:40 -05:00
Vedran Pavic
903cac492e Use Flapdoodle Embedded MongoDB for integration tests and samples 2016-03-25 07:27:33 +01:00
Rob Winch
3980be349b Merge pull request #440 from lowzj/master_fix-typo
Fix SessionRepositoryFilter comment typo
2016-03-23 08:27:23 -05:00
lowzj
128e0c4d47 Fix SessionRepositoryFilter comment typo 2016-03-23 19:32:05 +08:00
Rob Winch
37bc6a352f Merge pull request #437 from vpavic/improve-build
Externalize H2 database dependency version
2016-03-20 11:10:24 -05:00
Vedran Pavic
b88f48f01d Externalize H2 database dependency version 2016-03-20 01:56:35 +01:00
Spring Buildmaster
028e277fc9 Next development version 2016-03-16 20:32:18 -07:00
Spring Buildmaster
904cdf4e65 Release version 1.2.0.RC1 2016-03-16 20:32:03 -07:00
Rob Winch
b58ea03a3b Polish JdkMongoSessionConverter
* Serializer/Deserializer -> Converter API
* Verify/Test null constructor arguments

Issue gh-431
2016-03-16 21:06:57 -05:00
Jakub Kubrynski
9014ac9060 JdkMongoSessionConverter supports custom ClassLoader
Fixes gh-431
2016-03-16 20:35:39 -05:00
Rob Winch
7a82915a98 Polish Mongo Documentation
* Externalize documentation code for testing
* Polish wording
* Start each new sentence with new line

Issue gh-430
2016-03-16 09:12:35 -05:00
Rob Winch
e16224583c Fix Eclipse copyright start date 2016-03-16 09:12:35 -05:00
Jakub Kubrynski
96861ea535 Extend Mongo documentation 2016-03-16 09:12:35 -05:00
Rob Winch
de3d04fb29 Merge pull request #429 from eddumelendez/gh-427
Update to Sonarqube Plugin
2016-03-15 08:41:02 -05:00
Eddú Meléndez
68823eec29 Update to Sonarqube Plugin
See gh-427
2016-03-15 07:59:18 +10:00
Rob Winch
a58d4f64fb Merge pull request #428 from vpavic/update-docs
Fix incorrect link in What's new section
2016-03-14 16:22:40 -05:00
Vedran Pavic
40ad82806c Fix incorrect link in What's new section 2016-03-14 22:08:30 +01:00
Rob Winch
6db7d45f65 Add gh-423 to What's New in 1.2
Issue gh-420
2016-03-14 13:34:38 -05:00
Rob Winch
bbbdf939e8 Polish JacksonMongoSessionConverter
Issue gh-416
2016-03-14 13:29:17 -05:00
Jakub Kubrynski
e23398b890 Add JacksonMongoSessionConverter
Fixes gh-416
2016-03-14 13:29:17 -05:00
Rob Winch
5cfbeae161 Add Travis Badge
Issue gh-394
2016-03-14 12:42:03 -05:00
Rob Winch
c6e71a90bb Add Travis
Fixes gh-394
2016-03-14 12:30:02 -05:00
Rob Winch
195228ff5a Fix checkstyle
Fixes gh-423
2016-03-14 10:41:40 -05:00
Øyvind Horneland
831d2f4152 SessionRepositoryFilter caches null session lookup
If a session cannot be found by id, we will cache that result for any
subsequent calls to getSession(false) for the duration of this request.

Fixes gh-423
2016-03-14 10:40:25 -05:00
Rob Winch
4f7728f5b5 Fix Checkstyle Eclipse Settings
Issue gh-417
2016-03-14 00:13:46 -05:00
Rob Winch
11e418526e Add What's new in 1.2
Fixes gh-420
2016-03-11 10:21:23 -06:00
Erin Drummond
49f24e493d Fix JdbcOperationsSessionRepository session cleanup
Make JDBC session cleanup logic actually cleanup expired sessions
as opposed to all sessions

Fixes gh-421
2016-03-11 09:28:18 -06:00
Vedran Pavic
bd6d5bf419 Add JDBC documentation
Fixes gh-415
2016-03-10 09:24:57 -06:00
Rob Winch
9b989c4a3e Merge pull request #414 from vpavic/fix-docs-test
Fix bad reference in IndexDocTests
2016-03-07 16:19:28 -06:00
Rob Winch
68ddcef83e Fix Javadoc 2016-03-07 16:08:54 -06:00
Rob Winch
2592905b41 Add Eclipse settings
Fixes gh-417
2016-03-07 15:37:22 -06:00
Rob Winch
4be47f6b40 Add Agregate checkstyle task
Issue gh-393
2016-03-07 15:37:12 -06:00
Rob Winch
35b5fcdc75 Refine Checkstyle
Refine the Checkstyle rules slightly

Fixes gh-393
2016-03-07 15:36:56 -06:00
Vedran Pavic
4d010b23b8 Add Checkstyle to build
Fixes gh-393
2016-03-07 15:36:44 -06:00
Rob Winch
f0200696ef Additional Checkstyle Fixes
Issue gh-393
2016-03-07 15:36:17 -06:00
Vedran Pavic
7f3302253b Prepare codebase to adhere to Checkstyle rules
Issue gh-393
2016-03-07 15:33:42 -06:00
Rob Winch
9e3bcafa75 Use @SpringBootApplication 2016-03-07 12:45:06 -06:00
Rob Winch
216bfd7355 MongoSessionConverter -> AbstractMongoSessionConverter
Using Abstract prefix follows conventions for abstract classes
beginning with Abstract. This also opens up the door to introduce
a MongoSessionConverter interface in the future.

Issue gh-17
2016-03-07 12:45:05 -06:00
Jakub Kubrynski
34cebc3df6 Add MongoOperationsSessionRepository
Fixes gh-17
2016-03-07 12:45:05 -06:00
Rob Winch
7b28b214ff Polish JdbcOperationsSessionRepository
* Fix whitepspaces
* Remove Override from interfaces

Issue gh-364
2016-03-07 09:25:30 -06:00
Rob Winch
8f8cfe5d79 JdbcOperationsSessionRepository uses ConversionService
Issue gh-364
2016-03-07 09:18:21 -06:00
Vedran Pavic
e0e29eab35 Fix bad reference in IndexDocTests 2016-03-06 23:18:51 +01:00
Vedran Pavic
cd38e307e0 Add JdbcOperationsSessionRepository
This commit provides implementation of SessionRepository based
on Spring's JdbcOperations interface.

@EnableJdbcHttpSession annotation is provided to ease the
configuration, together with spring-session-jdbc BOM and schema
creation scripts for all major databases.

Fixes gh-364
2016-03-04 11:24:06 -06:00
Rob Winch
091d0d8d9f DefaultCookieSerializer ignores null Cookie value
Previously a null Cookie value was returned by DefaultCookieSerializer
readCookieValues(). This could cause NullPointerExeptions later on.

This commit ignores cookies with a null value.

Fixes gh-392
2016-03-04 10:22:33 -06:00
John Blum
3300b58afe Fix GemFire TODOs
Upgrades Spring Session to use Spring Data GemFire 1.7.4.RELEASE
and addresses all TODOs.

Fixes gh-403
2016-02-29 21:07:00 -06:00
John Blum
00af8456d4 @DirtiesContext for GemFire integration tests
Applies @DirtiesContext to all Spring Session Data GemFire integration
tests to ensure that no lingering GemFire (peer or client) cache
instances cause conflicts on downstream tests.

Fixes gh-396
2016-02-29 20:46:59 -06:00
Rob Winch
aeb0add50e Fix GemFire compile in Eclipse
Fixes gh-399
2016-02-29 15:47:01 -06:00
Rob Winch
671bfa0444 Revert "Fix GemFire compile in Eclipse"
This reverts commit 8bf3a251bb.
since the fixes to the tests are incorrect.
2016-02-29 15:46:44 -06:00
Rob Winch
8bf3a251bb Fix GemFire compile in Eclipse
Fixes gh-399
2016-02-29 14:38:18 -06:00
Rob Winch
5d1c60711f Merge pull request #397 from vpavic/update-docs
Fix minor documentation errors
2016-02-29 12:59:41 -06:00
Vedran Pavic
32cf8ad7ad Fix minor documentation errors 2016-02-28 22:25:42 +01:00
Rob Winch
d3139b04e1 Update to 1.2.0.BUILD-SNAPSHOT 2016-02-25 14:40:17 -06:00
Spring Buildmaster
cc0bfc2299 Next development version 2016-02-25 11:15:24 -08:00
Spring Buildmaster
c0a5916413 Release version 1.1.0.RELEASE 2016-02-25 11:15:17 -08:00
Rob Winch
11ca552625 Update Dependency Versions
Fixes gh-386
2016-02-25 13:08:44 -06:00
Rob Winch
8e4ed22974 Add gradle-versions-plugin
This will make finding updates easier.

Fixes gh-385
2016-02-24 17:05:52 -06:00
John Blum
53d7c84f73 Fix ClassCastException Token$Tombstone
Fixes gh-373
2016-02-24 17:01:26 -06:00
Rob Winch
1871915c62 Update SessionEventRegistry to store all events
This ensures that if multiple events are published, we wait until the event
with the expected session id is fired.

Fixes gh-382
2016-02-23 15:41:57 -06:00
Rob Winch
d5259bf0e9 Fix RedisSessionExpirationPolicy logger name
Fixes gh-329
2016-02-23 14:31:00 -06:00
Rob Winch
12b1dda24c Remove EnableScheduling from SpirngHttpsessionConfiguration
Fixes gh-377
2016-02-22 22:43:15 -06:00
Rob Winch
fad66c5cd2 Spaces to Tabs 2016-02-22 22:12:47 -06:00
Rob Winch
e338e70972 Remove unnecessary JS from boot sample
Fixes gh-378
2016-02-22 22:03:43 -06:00
Rob Winch
4c01782d6c Polish gh-314
* Formatting
* Place lock inside test object
2016-02-22 13:23:42 -06:00
Vladimir Tsanev
5bf12fdd93 Custom task executors for RedisMessageListenerContainer
Tasks executors used by redisMessageListenerContainer can now optionally
be overriden with beans named springSessionRedisTaskExecutor and/or
springSessionRedisSubscriptionExecutor.

Fixes gh-314
2016-02-22 13:22:53 -06:00
Rob Winch
d209a10514 Clean up principal index immediately on delete
Previously index was cleaned up only in the Redis Keyspace Notification.
This meant there was a delay in removing the index. This does not cause
a bug since we verify sessions exist and are not expired when we look up
sessions by index. However, it could be improved.

This commit ensures that the index is cleaned up immediately on session
deletion.

Fixes gh-367
2016-02-15 08:32:59 -06:00
Spring Buildmaster
1d0eb68f70 Next development version 2016-02-10 21:17:19 -08:00
Spring Buildmaster
b64d0c0d3c Release version 1.1.0.RC1 2016-02-10 21:17:11 -08:00
Rob Winch
305ce1ae65 Update What's New Section
Fixes gh-341
2016-02-10 22:29:03 -06:00
Rob Winch
aed79ddbf7 Fix wait time in expireFiresSessionExpiredEvent 2016-02-10 10:23:05 -06:00
Rob Winch
3b5fc5e9cc Fix Hazelcast saveUpdatesTimeToLiveTest 2016-02-10 10:20:54 -06:00
Rob Winch
e0bbbbcd45 Add RedisFlushMode
This commit allows Redis support to flush lazily (already
supported) or immediately (new).

Fixes gh-273
2016-02-10 08:49:01 -06:00
Rob Winch
5f23b3c272 Null check for hazelcastInstance
Previously if the hazelcastInstance failed to start we would get a
NPE when the tests ended. This would mask the original issue of starting
the hazelcastInstance.

This commit ensures that hazelcastInstance is non-null before trying to
shut it down.

Polish gh-360
Relates to gh-360
2016-02-09 09:08:24 -06:00
Artem Bilan
bf7729b936 Close only used HazelcastInstance
* It might be dangerous to shutdownAll() HazelcastInstances from the
test-case, especially when we are ran in with the integration test and on
the CI environment.
Therefore extract the instance variable in the test and close only it from
the @AfterClass
* Fix typo in the AbstractHazelcastRepositoryITests test method name
* Increase lock wait timeout to the 10 seconds in the SessionEventRegistry.
Looks like 3 seconds isn't enough for my Windows machine.

Fixes gh-358 gh-360
2016-02-09 09:08:09 -06:00
Vedran Pavic
7c53558454 Add integration tests for Hazelcast client use case
Fixes gh-356
2016-02-08 16:16:22 -06:00
Mark Anderson
f2443f5e21 Support External Hazelcast instance
Previously the Hazelcast support only worked with
embedded Hazelcast instances.

This commit ensures that Hazelcast support works
with external Hazelcast instances.

Fixes gh-339
2016-02-08 16:00:37 -06:00
Rob Winch
2b5386ad98 Polish GemFire findByIndexNameAndValue
Issue gh-353
2016-02-08 15:36:42 -06:00
John Blum
c2b407189e Add generic GemFire findByIndexNameAndIndexValue support
Fixes gh-353
2016-02-08 15:36:08 -06:00
Rob Winch
7de11753a9 Add Debug Logging for Session Creation
Fixes gh-323
2016-02-04 15:40:50 -06:00
Rob Winch
2d6a474c2c Change RedisSerializer qualifier to springSessionDefaultRedisSerializer
Fixes gh-350
2016-02-04 15:23:05 -06:00
Rob Winch
cc1d3a7e9e Add test for Redis onMessage w/ custom serializer
Issue gh-309
2016-02-04 14:12:24 -06:00
Janne Valkealahti
fe17e3fcc0 Use correct RedisSerializer
- Remove hardcoded JdkSerializationRedisSerializer from onMessage
  in favour of same defaultRedisSerializer user configured as this
  is most likely the one which works.
- Fixes #309
2016-02-04 14:01:26 -06:00
Rob Winch
ef904a4544 Add Code of Conduct
Fixes gh-351
2016-02-01 14:27:19 -06:00
Rob Winch
207e4e27b3 Add Gitter
Fixes gh-349
2016-01-31 23:00:11 -06:00
Rob Winch
bda5c85acf Cookie Value now sessionId + "." jvmRoute
If jvmRoute is set on DefaultCookieSerializer, then it sets the cookie
value to be:

sessionId + "." jvmRoute

Fixes gh-326
2016-01-29 16:50:22 -06:00
Rob Winch
c9acd3c812 Remove Compiler Warnings 2016-01-29 16:27:40 -06:00
Rob Winch
f20acbf9b9 Automatically Index SecurityContext
Fixes gh-266
2016-01-29 16:27:19 -06:00
Rob Winch
ad09b498a3 FindByPrincipalNameSessionRepository -> FindByIndexNameSessionRepository
Fixes gh-342
2016-01-29 13:54:11 -06:00
Rob Winch
7618aafb90 Fix Javadoc warnings
Fixes gh-347
2016-01-29 13:23:35 -06:00
Rob Winch
7a3bf533b3 Remove Compiler Warnings
Fixes gh-346
2016-01-29 12:13:03 -06:00
Rob Winch
723ea5e2e0 Remove GemFire ClientConfig compile time warning
Issue gh-346
2016-01-29 07:50:09 -06:00
Rob Winch
7463592988 Fix RedisOperationsSessionRepository.findByPrincipalName index
Previously, the index was not properly cleaned up when it was changed. This
commit ensures that the index is removed when the index name changes.

Fixes gh-343
2016-01-28 21:42:29 -06:00
Rob Winch
198acc0648 Remove compile time warning
Remove the MapSession.getAttribute compile time warning.

Issue gh-346
2016-01-28 21:42:00 -06:00
Rob Winch
f53e991d02 Remove spring-shell from spring-data-gemfire
Fixes gh-344
2016-01-28 20:26:49 -06:00
Rob Winch
22f6f9dd72 Polish GemFire
* Remove Compile Warnings
* Correct @since
* Add tests
* Add to What's New in documentation
2016-01-28 16:34:40 -06:00
John Blum
019e0083b0 Add GemFire Support
Fixes GH PR #148 and PR #308 implementing a GemFire Adapter to support
clustered HttpSessions in Spring Session.

* Resolve SGF-373 - Implement a Spring Session Adapter for GemFire backing
a HttpSession similar to the Redis support.
* Add Spring Session annotation to enable GemFire support with
@EnableGemFireHttpSession.
* Add extesion of SpringHttpSessionConfiguration to configure GemFire using
GemFireHttpSessionConfiguration.
* Add implementation of SessionRepository to access clustered, replicated
HttpSession state in GemFire with GemFireOperationsSessionRepository.
* Utilize GemFire Data Serialization framework to both replicate
HttpSession state information as well as handle deltas.
* Utilize GemFire OQL query to lookup arbitrary Session attributes by name,
and in particular the user authenticated principal name.
* Implment unit and integration tests, and in particular, tests for both
peer-to-peer (p2p) and client/server topologies.
* Set initial Spring Data GemFire version to 1.7.2.RELEASE, which depends
on Pivotal GemFire 8.1.0.
* Add documentation, Javadoc and samples along with additional Integration
Tests.

Fixes gh-148
2016-01-28 16:17:54 -06:00
Rob Winch
1931da83a5 Fix spring io tests
Issue gh-338
2016-01-25 20:42:07 -06:00
Rob Winch
5dff0ff37b Migrate from fest-assert to assertj
Fixes gh-338
2016-01-25 19:41:54 -06:00
Rob Winch
1003821dad Merge pull request #337 from anujbahuguna/master
Upgrade to Gradle Wrapper 2.10
2016-01-24 22:19:03 -06:00
Anuj Bahuguna
5399b7c299 Upgrade to Gradle Wrapper 2.10 2016-01-23 12:20:05 +00:00
Rob Winch
969053fa87 SessionRepositoryMessageInterceptor updates lastAccessedTime
Fixes gh-325
2015-12-08 08:37:50 -06:00
Rob Winch
eb646ef5d8 SessionRepositoryFilter sets maxInactiveIntervalInSeconds on create
Fixes gh-324
2015-12-04 15:44:52 -06:00
Rob Winch
f14942edb0 Add Tests for Lazy Updates of Spring Session
Ensure that the session is only modified if it is accessed. This allows for
optimizations to ensure that for things like static resources there is
no need for hitting a data store.
2015-12-04 15:44:52 -06:00
Rob Winch
18a07abeb4 Merge pull request #316 from tsachev/gh-315
Don't send expirations del events when expiration time does not change.
2015-12-03 16:57:51 -06:00
Rob Winch
e766ad774d Make ServletContext Optional
Fixes gh-318
2015-12-03 16:46:41 -06:00
Rob Winch
b38cc74764 Include alias unless single id and default alias
Fixes gh-321
2015-12-03 14:38:25 -06:00
Rob Winch
3df40dd8a9 Merge pull request #319 from izeye/patch-2
Upgrade to Gradle Wrapper 2.9
2015-11-28 14:18:56 -06:00
Johnny Lim
9d95638cf8 Upgrade to Gradle Wrapper 2.9 2015-11-28 11:37:02 +09:00
Vladimir Tsanev
d85624c58e Don't send expirations del events when expiration time does not change.
Expiration time is rounded up, so in case of many request per second from a
single session we can skip sending of delete event to reduce the traffic.
This is good because actually at the moment the redis repository is subscribed
for this events, even though it ignores them.

Fix gh-315
2015-11-26 14:43:31 +02:00
Rob Winch
5ca6377015 Next development version 2015-11-17 21:13:02 -06:00
Spring Buildmaster
33834fdf19 Release version 1.1.0.M1 2015-11-17 12:00:57 -08:00
Rob Winch
8e60c968a1 Add What's New in 1.1 Documentation
Fixes gh-304
2015-11-17 13:32:00 -06:00
Rob Winch
41298c490e Remove warnings 2015-11-17 11:03:36 -06:00
Rob Winch
ee09a9e863 Add ExpiringSession setLastAccessedTime
Previously SessionRepository had to update the lastAccessTime when it was
loaded. This prevented inspecting the last access time. For example,
listing all the sessions for a specific user. Furthermore, it is
unintuitive that a read operation would update attributes on the domain
model.

This change introduces ExpiringSession setLastAccessedTime to allow setting
the expiration on the interface. This means that the SessionRepositoryFilter
can update the last accessed time.

Fixes gh-272
2015-11-17 10:47:42 -06:00
Rob Winch
8bd29c1ab2 HttpSessionListener sessionCreated id contains invalid ':'
Fixes gh-305
2015-11-16 16:12:35 -06:00
Tommy Ludwig
f1d0350356 Remove unnecessary code
Code for a workaround that is no longer necessary was copied from
existing code. This removes that unnecessary code.
2015-11-16 11:21:31 -06:00
Rob Winch
2fa7ecdee5 Remove unnecessary RedisHttpSessionConfiguration code
Fixes gh-306
2015-11-16 11:13:31 -06:00
Rob Winch
8a5cba914b RedisOperationsSessionRepsitory only deletes once
Previously RedisOperationsSessionRepository incorrectly:

* Deleted the session
* Added the session
* Set the expiration to be 0

This commit ensures that if the expiration is 0 that the sesson is only
deleted.

Fixes gh-292
2015-11-16 10:52:39 -06:00
Rob Winch
5fbf333c61 Fix RedisOperationsSessionRepository Logger name 2015-11-16 10:26:44 -06:00
Rob Winch
33c9485031 Deprecate SessionMessageListener
Deprecate SessionMessageListener in favor of
RedisOperationsSessionRepository

Fixes gh-289
2015-11-16 10:25:55 -06:00
Rob Winch
2e82b311ee Fix custom MultiHttpSessionStrategy configuration
Fixes gh-303
2015-11-11 15:27:01 -06:00
Rob Winch
8c07537bec Add CookieSerializer Strategy
This allows for custom seralization of the Cookie.

Fixes gh-299
2015-11-11 15:25:27 -06:00
Tommy Ludwig
c701e1877e Fix NPE with EnableHazelcastHttpSession and add tests
This fixes the NPE caused by setting maxInactiveIntervalInSeconds = ""
on the EnableHazelcastHttpSession annotation. This is a basic scenario
that was not covered by integration tests. Tests configuring Hazelcast
with an XML file (probably the most common method in the wild) covering
the failing scenario and more have been added.
2015-11-09 10:10:39 -06:00
Rob Winch
7186878f84 Update to Gradle 2.9 rc1
Fixes gh-298
2015-11-05 09:30:12 -06:00
Rob Winch
aeef1417e1 Display Javadoc Errors 2015-11-05 09:30:12 -06:00
Rob Winch
76d341d6ca Polish Hazelcast
* This commit moves the hazelcast support into a parent package
so that it no longer impies a Spring Data dependency.
* Add guards on SessionEntryListener logger
* Remove getSessionMapName on HazelcastHttpSessionConfiguration
* Use setSessionMapName on HazelcastHttpSessionConfiguration
rather than field access
* Formatting polish
* Fix Javadoc

Issue gh-276
2015-11-05 09:30:03 -06:00
Tommy Ludwig
d1c00c6080 Add Hazelcast
Fixes gh-276
2015-11-04 16:13:20 -06:00
Rob Winch
a48864bf20 Fix EnableSpringHttpSession @since
Issue gh-231
2015-10-01 15:42:09 -05:00
Rob Winch
bebb596846 Merge pull request #284 from manderson23/master
Minor documentation fix
2015-08-31 15:42:19 -05:00
Mark Anderson
af5899fb1a Fix inconsistency in docs when describing delete/expired events for Redis. 2015-08-31 21:17:30 +01:00
Rob Winch
489a26c14c Allow default RedisSerializer to be specified
Fixes gh-283
2015-08-31 10:26:48 -05:00
Rob Winch
134d23bb07 Add jsp-api to security sample
Fixes gh-279
2015-08-25 15:11:28 -05:00
Rob Winch
8b5469ef5c Polish hazelcast-spring sample
* Add author for Mark Anderson created classes
* Add Apache License
* Add jsp-api dependency

Issue gh-274
2015-08-25 15:10:38 -05:00
Mark Anderson
e3414ef11e Add hazelcast-spring Sample
Fixes gh-274
2015-08-25 15:10:10 -05:00
Rob Winch
68ad464b67 Add minimal Contributor guidelines 2015-08-25 09:14:38 -05:00
Rob Winch
db2759c497 Add Eclipse Code Formatter 2015-08-19 20:45:00 -05:00
Rob Winch
8760d7037a Polish RedisOperationsSessionRepositoryITests 2015-08-19 11:52:44 -05:00
Rob Winch
d2f7ac4898 Polish RedisOperationsSessionRepositoryITests
* Add namespace so we don't receive events for other tests
* Delete Sessions so don't receive events later
* Clear Registry before every test
2015-08-19 11:43:25 -05:00
Rob Winch
5fd2c71559 Minimize MapSession's use of Secure Random
Previously when creating a MapSession from an existing session required
that UUID.randomUUID() be invoked. This could slow down the system
since it requires entropy.

MapSession now has a constructor that accepts the id which prevents
Secure random from being used when the session is already known.

Fixes gh-271
2015-08-19 11:38:17 -05:00
Rob Winch
6414d0ddb8 Update to 1.1 2015-08-18 15:18:53 -05:00
Rob Winch
ba97bdf2fa EnableSpringHttpSession updated to since 1.2 2015-08-18 15:10:58 -05:00
Rob Winch
6c82628402 Fix FindByUsernameSessionRepository indent
Issue gh-7
2015-08-18 12:42:12 -05:00
Rob Winch
16b65973b7 Add EnableSpringHttpSession
Fixes gh-231
2015-08-18 12:41:50 -05:00
Rob Winch
db45698e25 Support application specific prefix
Fixes gh-166
2015-08-17 14:44:22 -05:00
Rob Winch
6234dd5681 Refactor tests
Prepare the tests for supporting an application prefix to ensure
there are no regressions.

Issue gh-166
2015-08-17 14:44:22 -05:00
Rob Winch
00d50593c1 Add livereload to docs/build.gradle
Fixes gh-269
2015-08-17 14:44:14 -05:00
Rob Winch
881ca7c2d4 Support querying for sessions by user identifier
Fixes gh-7
2015-08-17 14:41:55 -05:00
Rob Winch
77eb6cfd71 Remove commons-httpclient:commons-httpclient
Fixes gh-268
2015-08-17 14:41:35 -05:00
Rob Winch
21065b23c0 Add HttpSessionListener Support
Fixes gh-4
2015-08-12 16:55:01 -05:00
Rob Winch
7693d0e624 Extract out ExpiringSessionHttpSession
This allows the HttpSession Spring Session adapter to be reused
for HttpSessionEvent.

Issue gh-4
2015-08-12 16:55:01 -05:00
Rob Winch
2503537e36 Fix reference indentation for REST subsections
Fixes gh-262
2015-08-12 16:55:01 -05:00
Rob Winch
d27d9a22bf Add SessionCreatedEvent
Fixes gh-261
2015-08-12 16:55:01 -05:00
Rob Winch
6e1ecadcc4 Add Sesison to SessionExpiredEvent
Fixes gh-260
2015-08-12 16:49:10 -05:00
Rob Winch
690fbc8d3f RedisOperations<String,ExpiringSession> -> RedisOperations<Object,Object>
The RedisOperations manages more than just ExpiringSessions, so we need
to make this more generice.

Fixes gh-259
2015-08-12 16:37:56 -05:00
Rob Winch
3ba07ec0a8 Polish add SessionDeletedEvent & SessionExpiredEvent
* Add @author Mark Anderson for proper credit
* SessionDestroyedEvent no longer abstract to preserve passivity

Issue gh-258
2015-08-12 16:34:35 -05:00
Mark Anderson
7518306975 Add SessionExpiredEvent and SessionDeletedEvent
Fixes gh-258
2015-08-12 16:24:18 -05:00
Spring Buildmaster
249361bb7f Next development version 2015-08-03 14:07:47 -07:00
Rob Winch
f633037d6b Document SessionRepsitoryFilter ordering
Fixes gh-172
2015-07-29 11:22:57 -05:00
Rob Winch
5dd36c95d6 Document Minimum Requirements
Fixes gh-115 gh-118 gh-119
2015-07-29 11:21:07 -05:00
Rob Winch
6b3f814cd6 Remove embeddedRedisVersion
Issue gh-248
2015-07-29 11:19:40 -05:00
Rob Winch
3a17a84434 Re-run Tests against Spring 3
Fixes gh-120
2015-07-29 11:18:51 -05:00
Rob Winch
916a5cb7dd Run integration tests in check task
Fixes gh-254
2015-07-29 11:17:18 -05:00
Rob Winch
d5484e15ca CookieHttpSessionStrategy only writes same Session once
If SessionRepositoryRequestWrapper.commitSession() is invoked twice
when a new session is created, then CookieHttpSessionStrategy will
add the same cookie twice. A couple examples of how this could happen:

* The response is committed and
  SessionRepositoryResponseWrapper.onResponseCommitted() invokes
  SessionRepositoryRequestWrapper.commitSession(). Then the finally
  block in SessionRepositoryFilter invokes
  SessionRepositoryRequestWrapper.commitSession() again.
* The new session is initialized and an Exception is thrown (i.e.
  gh-229). The SessionRepositoryFilter invokes
  SessionRepositoryRequestWrapper.commitSession() in the REQUEST
  dispatch. Then in the ERROR dispatch SessionRepositoryFilter invokes
  SessionRepositoryRequestWrapper.commitSession() invokes it again.

This commit ensures if the same Session is passed into
CookieHttpSessionStrategy multiple times within the same HttpServletRequest
it is only written once by keeping track of the sessions on a request
attribute.

Fixes gh-251
2015-07-28 15:01:51 -05:00
Rob Winch
3d93f2cf56 currentSession saved on HttpServletRequest attribute
Previously, if the following happened:

* New Session Created
* Exception thrown
* Exception processed by error handler within Servlet
* Error Handler used a session

The result would be two sessions were created. This means the
data from the first session was also lost. This happend
because ERROR dispatch is a separate Filter invocation where
the request is no longer wrapped.

This commit ensures that currentSession is saved on a
HttpServletRequest attribute so that the ERROR dispatch sees
that a session was already created.

Fixes: gh-229
2015-07-27 17:23:34 -05:00
Rob Winch
8929e85fb1 Fix Tests
These tests passed, but were technically incorrect. The
invalid tests were noticed when fixing gh-229.

Issue: gh-229
2015-07-27 17:11:04 -05:00
Rob Winch
f6c1407259 Ignore .rdb files 2015-07-27 15:26:18 -05:00
Rob Winch
b79913240d HttpServletRequest.changeSessionId() impacts previous references
Previously, if a user had a reference to an existing HttpSession and
changed the session id, it would not work. For example:

HttpSession s = request.getSession();
request.changeSessionId();
s.setAttribute(...);

This commit fixes holding on to a reference of an HttpSession when
the session id is changed.

Fixes gh-227
2015-07-27 15:25:58 -05:00
Rob Winch
4c24384243 Remove generic type from SpringSessionRepositoryFilterTests
If there is a generic type in tests, Eclipse cannot figure out that
it is a test. This makes it difficult to run in the IDE.
2015-07-27 15:13:49 -05:00
Rob Winch
1635ea90ca enableRedisKeyspaceNotificationsInitializer return type InitializingBean
Previously RedisHttpSessionConfiguration
enableRedisKeyspaceNotificationsInitializer return type was a package
protected class. This meant if someone extended
RedisHttpSessionConfiguration they got IllegalAccessErrors.

This changes the return type to InitializingBean.

Fixes gh-109
2015-07-25 08:09:51 -05:00
Rob Winch
9724953b2f Rename local var to enableRedisHttpSessionAnnotation
From enableWebSecurityAnnotation to enableRedisHttpSessionAnnotation.

Fixes gh-194
2015-07-25 07:44:21 -05:00
izeye
1266fd052e Remove duplicate var headers = {};
Fixes gh-197
2015-07-25 07:37:08 -05:00
Adib Saikali
6c970efb09 Fix <type> to </type> in Maven section of guides
Fix typo in maven dependiecs of some of the the guides
Cutting and pasting the maven dependecies from the guides was failing
due to an xml element that was not closed.

Fixes gh-211
2015-07-25 07:34:22 -05:00
Jean-Pierre Bergamin
a32ee3935d Add projects subdomain in project page URL
While the projects subdomain is not necessary due to a redirect
it does provide a faster experience for users.

This commit adds the projects subdomain to the project URL to
avoid unnecessary redirects.

Fixes gh-220
2015-07-25 07:33:43 -05:00
Slavisa Avramovic
cc0c6bda42 Fix Hazelcast Sample attributeValue -> attributeName
The id attribute should have been attributeName instead of
attributeValue

Fixes gh-221
2015-07-24 17:02:25 -05:00
Rob Winch
1bee69ef15 Add tests for SessionRepositoryFilter set HttpSessionStrategy null
Issue gh-224
2015-07-24 16:53:00 -05:00
james
3b0dd129ba Fix SessionRepository setHttpSessionStrategy Validationi
Fixes gh-224
2015-07-24 16:52:23 -05:00
Geoffrey Tucker
c3c6215a24 Remove EnableRedisKeyspaceNotificationsInitializer.CONFIG_NOTIFY_KEYSPACE_EVENTS
There's a duplicate of this field in
org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction which is
what is actually used.

Fixes gh-225
2015-07-24 16:42:05 -05:00
Rob Winch
8eca5ea4c6 Remove Embedded Redis
Fixes gh-248
2015-07-24 15:40:55 -05:00
Rob Winch
e4c74ebe91 Add MockMvc tests to rest sample 2015-07-09 15:12:22 -05:00
Andy Wilkinson
141aba9526 Use the latest version of the Spring IO Plugin
Spring IO Platform 2.0 will remove the managed versions .properties
file as support for it has been removed in Spring Boot 1.3.

This commit moves the build onto a new version of the Spring IO Plugin
that uses the Maven bom rather than the properties file.
2015-07-08 08:33:15 -05:00
Rob Winch
f5bb381191 Merge pull request #195 from manderson23/hazelcast
Replace "a Redis instance" with "Hazelcast" in index.jsp
2015-04-20 15:08:59 -05:00
Mark Anderson
b9151c6908 Replace "a Redis instance" with "Hazelcast" in index.jsp for Hazelcast sample. 2015-04-19 20:31:14 +01:00
Rob Winch
664b5aae21 Add EnableEmbeddedRedis to WebSocket Sample
This issue is a result of moving EmbeddedRedisConfiguration to its own
package in 865e381c7c.

Now we explicitly add EnableEmbeddedRedis to the configuration.

Fixes gh-191
2015-04-16 16:59:35 -05:00
Spring Buildmaster
b44b7365b5 Next development version 2015-04-15 13:43:24 -07:00
Rob Winch
23afc1b354 Fix RedisSessionExpirationPolicy to properly cleanup expired sessions
Previously forcibly cleaning up sessions was not working. All the cleanup
was done by Redis expiration. This meant that sessions would be kept alive
until Redis cleaned them up (non deterministic).

This commit resolves the mapping of expiration to session ids.

Fixes gh-169
2015-04-15 15:25:50 -05:00
Rob Winch
f711876347 Add test assert SessionRepository not invoked unless neededwq 2015-04-15 12:45:54 -05:00
Rob Winch
5879d15692 Valid HTTP session no longer invalidated when HttpSession not accessed
Fixes gh-188
2015-04-15 12:33:34 -05:00
Rob Winch
30d4ea996b Fix doc dependencies for gh-124 2015-04-14 22:46:02 -05:00
Rob Winch
d2e5005fa0 Allow disabling configuration of Redis
Fixes gh-124
2015-04-14 22:34:01 -05:00
Rob Winch
865e381c7c Move Embedded Redis classes to org.springframework.session.redis.embedded
This prevents the embedded redis from being picked up in ComponentScan
in any of the samples unless the @EnableEmebeddedRedis annotation is used.

Polish gh-184
2015-04-14 21:59:19 -05:00
Rob Winch
8c1f1c4c52 Update to embedded Redis 0.6
Fixes gh-187
2015-04-14 15:32:57 -05:00
Rob Winch
c2414c498f Polish httpsession.adoc 2015-04-10 13:47:53 -05:00
Rob Winch
4f8588b4bf Add HttpSession XML Guide
Fixes gh-139
2015-04-10 13:47:37 -05:00
Rob Winch
98cb22c670 Fix broken imports
Issues gh-184
2015-04-09 21:11:19 -05:00
Rob Winch
4a0aa6e608 Add EnableEmbeddedRedis
Fixes gh-184
2015-04-09 17:02:56 -05:00
Rob Winch
88151b4287 Fix rest sample tests
- move to itest folder
- dynamically allocate the port for Redis

Issues gh-183
2015-04-09 14:28:12 -05:00
Rob Winch
3a42147d3e Add NullRequestCache to rest sample
Fixes gh-183
2015-04-08 16:59:38 -05:00
Rob Winch
4631b57531 Make Servlet 2.5 compatible
Fixes gh-111 gh-182
2015-04-07 22:12:40 -05:00
Rob Winch
5417e59a50 Test against latest Spring IO snapshot
Fixes gh-181
2015-04-07 16:33:53 -05:00
Rob Winch
9ac570cba7 Update to Spring IO version
- Update Spring Boot 1.2.3.RELEASE
- Update Spring Security 4.0.0.RELEASE
- Update Spring 4.1.6.RELEASE
- Update Groovy 2.3.8
- Update Jackson 2.4.5
- Update Jedis 2.5.2
- Update Spring Data Redis 1.4.2

Fixes gh-126
2015-04-07 16:30:57 -05:00
Rob Winch
3d1cd9fae4 Implement changeSessionId()
Fixes gh-152
2015-04-03 17:07:21 -05:00
Rob Winch
3c72828500 SessionRepositoryFilterTests better setup of next request 2015-04-03 16:11:41 -05:00
Rob Winch
d3ab764c76 Update Boot sample to use Redis Starter
Fixes gh-125
2015-04-03 13:15:23 -05:00
Rob Winch
eddb10d52c Make RedisHttpSessionConfiguration more XML friendly
* add setMaxInactiveIntervalInSeconds
* add default value for maxInactiveIntervalInSeconds

Fixes gh-105
2015-04-03 13:00:27 -05:00
Rob Winch
208493b6fd Update to embedded 0.5
Fixes gh-179
2015-04-03 12:07:49 -05:00
Rob Winch
d46bc636de Add comment to end::how-does-it-work[]
Fixes gh-145
2015-04-03 11:13:13 -05:00
Rob Winch
4dedb4d10a Spaces to tabs and license cleanup 2015-04-03 11:13:13 -05:00
Rob Winch
93b8856a20 Merge pull request #178 from Eyads/patch-1
Fix typo in documentation

You can the basic steps -> You can follow the basic steps
2015-03-31 10:23:37 -05:00
Eyad Salamin
07e1c91f1c typo
You can the basic steps -> You can follow the basic steps
2015-03-31 09:41:51 +04:00
Rob Winch
2d8664d841 OnCommittedResponseWrapper tracks getOutputStream().write(byte[]) properly
Fixes gh-171
2015-03-10 00:29:26 -05:00
Rob Winch
72227b8d83 Merge pull request #157 from domdorn/javadoc_fixes
fixed javadoc errors
2015-02-23 10:28:28 -06:00
Rob Winch
34e2faa692 Merge pull request #156 from domdorn/patch-1
Upgrade to embedded-redis:0.4 (fixes #113)
2015-02-23 10:17:43 -06:00
Dominik Dorn
6347c32f7b fixed javadoc errors 2015-02-23 12:45:41 +01:00
Dominik Dorn
a7f686af73 Upgrade to embedded-redis:0.4 (fixes #113)
Embedded-Redis 0.4 got released; it now includes the latest versions of redis for win/mac/unix and 
therefor fixes the problems with 
ERR Unsupported CONFIG parameter: notify-keyspace-events #113
2015-02-23 12:09:12 +01:00
Rob Winch
020fbdaf4f Polish gh-154 2015-02-19 17:06:25 -06:00
Rob Winch
369e98c7ef SessionRepositoryRequestWrapper overrides isRequestedSessionIdValid
Fixes gh-142, gh-153
2015-02-19 16:49:38 -06:00
Dave Syer
29ad238307 Invoke HttpSessionStrategy.onNewSession if session id changed
Fixes gh-154
2015-02-19 16:49:38 -06:00
Rob Winch
ae03fac468 Merge pull request #123 from danveloper/master
pump spring-data-redis version
2015-01-26 12:47:30 -06:00
Daniel Woods
9534f9ee3a pump spring-data-redis version 2015-01-25 11:23:51 -08:00
Rob Winch
a25da734c1 Merge pull request #117 from brixtonasias/master
Small documentation improvements
2015-01-20 10:09:54 -06:00
Stefan Kohler
0ac460563f Make 'Filter' look more code-like 2015-01-20 17:03:22 +01:00
Stefan Kohler
8dd885a6c7 Add missing verb in note in the RESTful guide 2015-01-20 16:41:50 +01:00
Stefan Kohler
4926606841 Remove arbitrary verb 2015-01-20 16:40:30 +01:00
Stefan Kohler
d4d8a4e6c9 Reword after a word too much was found
Removed 'a' after the if and reworded sentence
2015-01-20 16:34:15 +01:00
Stefan Kohler
61a2586aed Add missing 'an' 2015-01-20 16:31:56 +01:00
Rob Winch
3be13ac10b Merge pull request #116 from brixtonasias/patch-1
Change gradlew run command to bootRun
2015-01-20 08:59:12 -06:00
Stefan Kohler
8dabbd26e5 Change gradlew run command to bootRun
In contrast to :tomcatRun :bootRun works for me.

For :tomcatRun I was getting: Task 'tomcatRun' not found in project ':samples:boot'.
2015-01-20 15:48:09 +01:00
Rob Winch
701f20cf04 Add named anchors to boot reference 2015-01-15 11:11:50 -06:00
Rob Winch
3f23643121 Polish Boot documentation
This removes the warnings produced by the invalid callout in the boot
reference. It also provides an example of how to customize the redis
connection and links to the "Connection to Redis" section of the
Spring Boot reference.

Issue #108
2015-01-15 11:11:33 -06:00
Roy Kachouh
37aafc5148 Update Spring boot sample documentation. 2015-01-15 10:44:12 -06:00
Roy Kachouh
afc3752b33 Remove unnecessary JedisConnectionFactory bean 2015-01-15 10:44:05 -06:00
Rob Winch
cf1a4b83bf Polish WebSocket doc cleanup
The callouts in the websocket guide need to be after the sample otherwise
the following warning is omitted:
asciidoctor: WARNING: guides/websocket.adoc: line 36: no callouts refer to list item 1
asciidoctor: WARNING: guides/websocket.adoc: line 38: no callouts refer to list item 2
asciidoctor: WARNING: websocket.adoc: line 36: no callouts refer to list item 1
asciidoctor: WARNING: websocket.adoc: line 38: no callouts refer to list item 2

This issue resolves the WARNING logs created due to the callout being
placed in the wrong location.

Issue #99
2015-01-15 10:41:54 -06:00
Rob Winch
ba07c1d562 Add httpsession-xml
Fixes #103
2015-01-14 11:40:49 -06:00
Rob Winch
f5e3deafa9 Merge pull request #99 from rstoyanchev/docs
Small improvement to WebSocket guide
2015-01-14 09:52:13 -06:00
Rossen Stoyanchev
43b393fb3f Small improvement to WebSocket guide
Simply move content around so that we first show what needs to be
done and then explain what is done internally. Arguably this is an
easier way to digest since the former is required knowledge while
the latter is optional.
2015-01-09 09:30:05 -05:00
Rob Winch
b5f96c3564 Fix REST guide formatting
A missing - caused the end of the REST guide to be escaped.

Fixes gh-96
2015-01-08 14:10:36 -06:00
Rob Winch
00d30f3dfd Next Development Version 2015-01-08 11:21:57 -06:00
Spring Buildmaster
97a05cf95d Next development version 2015-01-08 08:56:21 -08:00
635 changed files with 39053 additions and 59855 deletions

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

@@ -0,0 +1,3 @@
<!--
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.
-->

3
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,3 @@
<!--
Thanks for contributing to Spring Session. Please provide a brief description of your pull-request and reference any related issue numbers (prefix references with #).
-->

6
.gitignore vendored
View File

@@ -8,4 +8,8 @@ bin
.project
target
out
.springBeans
.springBeans
*.rdb
!eclispe/.checkstyle
.checkstyle
!**/src/**/build

20
.travis.yml Normal file
View File

@@ -0,0 +1,20 @@
language: java
sudo: required
services: docker
jdk: oraclejdk8
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
install: true
script: ./gradlew clean build --refresh-dependencies --no-daemon

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
http://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
http://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]

26
CONTRIBUTING.adoc Normal file
View File

@@ -0,0 +1,26 @@
= Contributing to Spring Session
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
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
We use GitHub issues to track bugs and enhancements. If you have a general usage question
please ask on http://stackoverflow.com[Stack Overflow]. The Spring Session team and the
broader community monitor the http://stackoverflow.com/tags/spring-session[`spring-session`]
tag.
If you are reporting a bug, please help to speed up problem diagnosis by providing as much
information as possible. Ideally, that would include a small sample project that
reproduces the problem.
== Sign the Contributor License Agreement
If you have not previously done so, please fill out and
submit the https://cla.pivotal.io/sign/spring[Contributor License Agreement].

88
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,88 @@
def projectProperties = [
[$class: 'BuildDiscarderProperty',
strategy: [$class: 'LogRotator', numToKeepStr: '5']],
pipelineTriggers([cron('@daily')])
]
properties(projectProperties)
def SUCCESS = hudson.model.Result.SUCCESS.toString()
currentBuild.result = SUCCESS
try {
parallel check: {
stage('Check') {
node {
checkout scm
try {
sh "./gradlew clean check --refresh-dependencies --no-daemon"
} catch(Exception e) {
currentBuild.result = 'FAILED: check'
throw e
} finally {
junit '**/build/*-results/*.xml'
}
}
}
},
springio: {
stage('Spring IO') {
node {
checkout scm
try {
sh "./gradlew clean springIoCheck -PplatformVersion=Cairo-BUILD-SNAPSHOT -PexcludeProjects='**/samples/**' --refresh-dependencies --no-daemon --stacktrace"
} catch(Exception e) {
currentBuild.result = 'FAILED: springio'
throw e
} finally {
junit '**/build/spring-io*-results/*.xml'
}
}
}
}
if(currentBuild.result == 'SUCCESS') {
parallel artifactory: {
stage('Artifactory Deploy') {
node {
checkout scm
withCredentials([usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')]) {
sh "./gradlew artifactoryPublish -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --no-daemon --stacktrace"
}
}
}
},
docs: {
stage('Deploy Docs') {
node {
checkout scm
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
}
}
}
}
}
} finally {
def buildStatus = currentBuild.result
def buildNotSuccess = !SUCCESS.equals(buildStatus)
def lastBuildNotSuccess = !SUCCESS.equals(currentBuild.previousBuild?.result)
if(buildNotSuccess || lastBuildNotSuccess) {
stage('Notifiy') {
node {
final def RECIPIENTS = [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']]
def subject = "${buildStatus}: Build ${env.JOB_NAME} ${env.BUILD_NUMBER} status is now ${buildStatus}"
def details = """The build status changed to ${buildStatus}. For details see ${env.BUILD_URL}"""
emailext (
subject: subject,
body: details,
recipientProviders: RECIPIENTS,
to: "$SPRING_SESSION_TEAM_EMAILS"
)
}
}
}
}

View File

@@ -1,3 +1,7 @@
image:https://badges.gitter.im/spring-projects/spring-session.svg[link="https://gitter.im/spring-projects/spring-session?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"]
image:https://travis-ci.org/spring-projects/spring-session.svg?branch=master["Build Status", link="https://travis-ci.org/spring-projects/spring-session"]
= Spring Session
Rob Winch
@@ -9,9 +13,13 @@ Spring Session aims to provide a common infrastructure for managing sessions. Th
** Pluggable strategy for determining the session id
** Easily keep the HttpSession alive when a WebSocket is active
== Code of Conduct
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
You can find the documentation, issue management, support, samples, and guides for using Spring Session at http://spring.io/spring-session/
You can find the documentation, issue management, support, samples, and guides for using Spring Session at http://projects.spring.io/spring-session/
= License

View File

@@ -1,63 +1,20 @@
buildscript {
repositories {
maven { url "https://repo.spring.io/plugins-release" }
}
dependencies {
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.7")
classpath("org.springframework.build.gradle:spring-io-plugin:0.0.3.RELEASE")
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2'
}
dependencies {
classpath 'io.spring.gradle:spring-build-conventions:0.0.4.RELEASE'
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
}
repositories {
maven { url 'https://repo.spring.io/plugins-release' }
}
}
apply plugin: 'io.spring.convention.root'
group = 'org.springframework.session'
description = 'Spring Session'
ext.springBootVersion = '1.1.10.RELEASE'
ext.JAVA_GRADLE = "$rootDir/gradle/java.gradle"
ext.MAVEN_GRADLE = "$rootDir/gradle/publish-maven.gradle"
ext.TOMCAT_GRADLE = "$rootDir/gradle/tomcat.gradle"
ext.releaseBuild = version.endsWith('RELEASE')
ext.snapshotBuild = version.endsWith('SNAPSHOT')
ext.milestoneBuild = !(releaseBuild || snapshotBuild)
apply plugin: 'sonar-runner'
apply plugin: 'base'
sonarRunner {
sonarProperties {
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.projectName", "Spring Session"
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
property "sonar.links.homepage", 'https://github.com/spring-projects/spring-session'
property "sonar.links.ci", 'https://build.spring.io/browse/SESSION'
property "sonar.links.issue", 'https://github.com/spring-projects/spring-session/issues'
property "sonar.links.scm", 'https://github.com/spring-projects/spring-session'
property "sonar.links.scm_dev", 'https://github.com/spring-projects/spring-session.git'
property "sonar.java.coveragePlugin", "jacoco"
}
}
task configDocsZip(dependsOn: [':docs:asciidoctor',':spring-session:javadoc']) << {
project.tasks.docsZip.from(project(':docs').asciidoctor) {
into('reference')
}
project.tasks.docsZip.from(project(':spring-session').javadoc) {
into('api')
}
}
task docsZip(type: Zip, dependsOn: 'configDocsZip') {
group = "Distribution"
baseName = "spring-session"
classifier = "docs"
description = "Builds -${classifier} archive containing api and reference " +
"for deployment."
}
artifacts {
archives docsZip
}

View File

@@ -1,44 +0,0 @@
apply plugin: 'java'
apply plugin: 'org.asciidoctor.convert'
asciidoctorj {
}
tasks.findByPath("artifactoryPublish")?.enabled = false
dependencies {
testCompile project(':spring-session'),
"org.springframework.data:spring-data-redis:$springDataRedisVersion",
"org.springframework:spring-websocket:${springVersion}",
"org.springframework:spring-messaging:${springVersion}",
'junit:junit:4.11',
'org.mockito:mockito-core:1.9.5',
"org.springframework:spring-test:$springVersion",
'org.easytesting:fest-assert:1.4',
"redis.clients:jedis:2.4.1"
}
asciidoctor {
def ghTag = snapshotBuild ? 'master' : project.version
def ghUrl = "https://github.com/spring-projects/spring-session/tree/$ghTag/"
attributes 'version-snapshot': snapshotBuild,
'version-milestone': milestoneBuild,
'version-release': releaseBuild,
'gh-url': ghUrl,
'gh-samples-url': "$ghUrl/samples/",
'download-url' : "https://github.com/spring-projects/spring-session/archive/${ghTag}.zip",
'spring-session-version' : version,
'spring-version' : springVersion,
'docs-test-dir' : rootProject.projectDir.path + '/docs/src/test/java/',
'samples-dir' : rootProject.projectDir.path + '/samples/',
'source-highlighter' : 'coderay',
'imagesdir':'./images',
'icons': 'font',
'sectanchors':'',
'idprefix':'',
'idseparator':'-',
'docinfo1':'true',
'revnumber' : project.version
}

View File

@@ -0,0 +1,45 @@
apply plugin: 'io.spring.convention.docs'
apply plugin: 'io.spring.convention.spring-test'
dependencies {
testCompile project(':spring-session-core')
testCompile project(':spring-session-data-redis')
testCompile project(':spring-session-hazelcast')
testCompile project(':spring-session-jdbc')
testCompile "org.springframework:spring-jdbc"
testCompile "org.springframework:spring-messaging"
testCompile "org.springframework:spring-webmvc"
testCompile "org.springframework:spring-websocket"
testCompile "org.springframework.security:spring-security-config"
testCompile "org.springframework.security:spring-security-web"
testCompile "org.springframework.security:spring-security-test"
testCompile "junit:junit"
testCompile "org.mockito:mockito-core"
testCompile "org.springframework:spring-test"
testCompile "org.assertj:assertj-core"
testCompile "com.hazelcast:hazelcast"
testCompile "io.lettuce:lettuce-core"
testCompile "javax.servlet:javax.servlet-api"
}
def versions = dependencyManagement.managedVersions
asciidoctor {
def ghTag = snapshotBuild ? 'master' : project.version
def ghUrl = "https://github.com/spring-projects/spring-session/tree/$ghTag"
attributes 'version-snapshot': snapshotBuild,
'version-milestone': milestoneBuild,
'version-release': releaseBuild,
'gh-url': ghUrl,
'gh-samples-url': "$ghUrl/samples/",
'download-url' : "https://github.com/spring-projects/spring-session/archive/${ghTag}.zip",
'spring-session-version' : version,
'spring-version' : versions['org.springframework:spring-core'],
'lettuce-version' : versions['io.lettuce:lettuce-core'],
'hazelcast-version' : versions['com.hazelcast:hazelcast'],
'docs-itest-dir' : rootProject.projectDir.path + '/docs/src/integration-test/java/',
'docs-test-dir' : rootProject.projectDir.path + '/docs/src/test/java/',
'docs-test-resources-dir' : rootProject.projectDir.path + '/docs/src/test/resources/',
'samples-dir' : rootProject.projectDir.path + '/samples/',
'session-jdbc-main-resources-dir' : project(':spring-session-jdbc').projectDir.path + '/src/main/resources/'
}

View File

@@ -0,0 +1,140 @@
= Spring Session - find by username
Rob Winch
:toc:
This guide describes how to use Spring Session to find sessions by username.
NOTE: The completed guide can be found in the <<findbyusername-sample, findbyusername application>>.
[[findbyusername-assumptions]]
== Assumptions
The guide assumes you have already added Spring Session using the built in Redis configuration support to your application.
The guide also assumes you have already applied Spring Security to your application.
However, we the guide will be somewhat general purpose and can be applied to any technology with minimal changes we will discuss.
[NOTE]
====
If you need to learn how to add Spring Session to your project, please refer to the listing of link:../#samples[samples and guides]
====
== About the Sample
Our sample is using this feature to invalidate the users session that might have been compromised.
Consider the following scenario:
* User goes to library and authenticates to the application
* User goes home and realizes they forgot to log out
* User can log in and terminate the session from the library using clues like the location, created time, last accessed time, etc.
Wouldn't it be nice if we could allow the user to invalidate the session at the library from any device they authenticate with?
This sample demonstrates how this is possible.
[[findbyindexnamesessionrepository]]
== FindByIndexNameSessionRepository
In order to look up a user by their username, you must first choose a `SessionRepository` that implements link:../#api-findbyindexnamesessionrepository[FindByIndexNameSessionRepository].
Our sample application assumes that the Redis support is already setup, so we are ready to go.
== Mapping the username
`FindByIndexNameSessionRepository` can only find a session by the username, if the developer instructs Spring Session what user is associated with the `Session`.
This is done by ensuring that the session attribute with the name `FindByUsernameSessionRepository.PRINCIPAL_NAME_INDEX_NAME` is populated with the username.
Generally, speaking this can be done with the following code immediately after the user authenticates:
[source,java,indent=0]
----
include::{docs-test-dir}docs/FindByIndexNameSessionRepositoryTests.java[tags=set-username]
----
== Mapping the username with Spring Security
Since we are using Spring Security, the user name is automatically indexed for us.
This means we will not have to perform any steps to ensure the user name is indexed.
== Adding Additional Data to Session
It may be nice to associate additional information (i.e. IP Address, the browser, location, etc) to the session.
This makes it easier for the user to know which session they are looking at.
To do this simply determine which session attribute you want to use and what information you wish to provide.
Then create a Java bean that is added as a session attribute.
For example, our sample application includes the location and access type of the session
[source,java,indent=0]
----
include::{samples-dir}boot/findbyusername/src/main/java/sample/session/SessionDetails.java[tags=class]
----
We then inject that information into the session on each HTTP request using a `SessionDetailsFilter`.
For example:
[source,java,indent=0]
----
include::{samples-dir}boot/findbyusername/src/main/java/sample/session/SessionDetailsFilter.java[tags=dofilterinternal]
----
We obtain the information we want and then set the `SessionDetails` as an attribute in the `Session`.
When we retrieve the `Session` by username, we can then use the session to access our `SessionDetails` just like any other session attribute.
[NOTE]
====
You might be wondering at this point why Spring Session does not provide `SessionDetails` functionality out of the box.
The reason, is twofold.
The first is that it is very trivial for applications to implement this themselves.
The second reason is that the information that is populated in the session (and how frequently that information is updated) is highly application dependent.
====
== Finding sessions for a specific user
We can now find all the sessions for a specific user.
[source,java,indent=0]
----
include::{samples-dir}boot/findbyusername/src/main/java/sample/mvc/IndexController.java[tags=findbyusername]
----
In our instance, we find all sessions for the currently logged in user.
However, this could easily be modified for an administrator to use a form to specify which user to look up.
[[findbyusername-sample]]
== findbyusername Sample Application
=== Running the findbyusername Sample Application
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
[NOTE]
====
For the sample to work, you must http://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.
====
----
$ ./gradlew :samples:findbyusername:tomcatRun
----
You should now be able to access the application at http://localhost:8080/
=== 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.
You should also see a listing of active sessions for the currently logged in user.
Let's emulate the flow we discussed in the <<About the Sample>> section
* Open a new incognito window and navigate to http://localhost:8080/
* Enter the following to log in:
** **Username** _user_
** **Password** _password_
* Terminate your original session
* Refresh the original window and see you are logged out

View File

@@ -0,0 +1,127 @@
= 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 <<httpsession-jdbc-boot-sample, httpsession-jdbc-boot sample application>>.
== 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"]
----
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
</dependency>
</dependencies>
----
Spring Boot provides dependency management for Spring Session modules, so there's no need to explicitly declare dependency version.
// tag::config[]
[[httpsession-jdbc-boot-spring-configuration]]
== Spring Boot Configuration
After adding the required dependencies, we can create our Spring Boot configuration.
Thanks to first-class auto configuration support, setting up Spring Session backed by a relational database is as simple as adding a single configuration property to your `application.properties`:
.src/main/resources/application.properties
----
spring.session.store-type=jdbc
----
Under the hood, Spring Boot will apply configuration that is equivalent to manually adding `@EnableJdbcHttpSession` annotation.
This 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.
Further customization is possible using `application.properties`:
.src/main/resources/application.properties
----
server.session.timeout= # Session timeout in seconds.
spring.session.jdbc.initializer.enabled= # Create the required session tables on startup if necessary. Enabled automatically if the default table name is set or a custom schema is configured.
spring.session.jdbc.schema=classpath:org/springframework/session/jdbc/schema-@@platform@@.sql # Path to the SQL file to use to initialize the database schema.
spring.session.jdbc.table-name=SPRING_SESSION # Name of database table used to store sessions.
----
For more information, refer to http://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/htmlsingle/#boot-features-session[Spring Session] portion of the Spring Boot documentation.
[[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/{spring-boot-version}/reference/htmlsingle/#boot-features-configure-datasource[Configure a DataSource] portion of the Spring Boot documentation.
[[httpsession-jdbc-boot-servlet-configuration]]
== Servlet Container Initialization
Our <<httpsession-jdbc-boot-spring-configuration,Spring Boot 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.
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.

View File

@@ -1,5 +1,5 @@
= Spring Session - Spring Boot
Rob Winch
Rob Winch, Vedran Pavić
:toc:
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using Spring Boot.
@@ -16,72 +16,63 @@ If you are using Maven, ensure to add the following dependencies:
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
</dependencies>
----
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]
----
<repositories>
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
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]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
Spring Boot provides dependency management for Spring Session modules, so there's no need to explicitly declare dependency version.
[[boot-spring-configuration]]
== Spring Configuration
== Spring Boot 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:
After adding the required dependencies, we can create our Spring Boot configuration.
Thanks to first-class auto configuration support, setting up Spring Session backed by Redis is as simple as adding a single configuration property to your `application.properties`:
[source,java]
.src/main/resources/application.properties
----
include::{samples-dir}boot/src/main/java/sample/config/HttpSessionConfig.java[]
spring.session.store-type=redis
----
<1> The `@EnableRedisHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
Under the hood, Spring Boot will apply configuration that is equivalent to manually adding `@EnableRedisHttpSession` annotation.
This 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 Redis.
<2> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
In our example, we are connecting to localhost on the default port (6379).
For more information on configuring Spring Data Redis, refer to the http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
Further customization is possible using `application.properties`:
.src/main/resources/application.properties
----
server.session.timeout= # Session timeout in seconds.
spring.session.redis.flush-mode= # Sessions flush mode.
spring.session.redis.namespace= # Namespace for keys used to store sessions.
----
For more information, refer to http://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/htmlsingle/#boot-features-session[Spring Session] portion of the Spring Boot documentation.
[[boot-redis-configuration]]
== Configuring the Redis Connection
Spring Boot automatically creates a `RedisConnectionFactory` that connects Spring Session to a Redis Server on localhost on port 6379 (default port).
In a production environment you need to ensure to update your configuration to point to your Redis server.
For example, you can include the following in your *application.properties*
.src/main/resources/application.properties
----
spring.redis.host=localhost
spring.redis.password=secret
spring.redis.port=6379
----
For more information, refer to http://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/htmlsingle/#boot-features-connecting-to-redis[Connecting to Redis] portion of the Spring Boot documentation.
[[boot-servlet-configuration]]
== Servlet Container Initialization
Our <<boot-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
Our <<boot-spring-configuration,Spring Boot 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.
@@ -89,18 +80,29 @@ Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `spring
Fortunately, Spring Boot takes care of both of these steps for us.
[[boot-sample]]
== boot Sample Application
== Boot Sample Application
The boot Sample Application demonstrates how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using Spring Boot.
The Boot Sample Application demonstrates how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using Spring Boot.
=== Running the boot Sample Application
[[boot-running]]
=== Running the Boot Sample Application
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
$ ./gradlew :samples:boot:tomcatRun
[NOTE]
====
For the sample to work, you must http://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.
====
----
$ ./gradlew :samples:boot:bootRun
----
You should now be able to access the application at http://localhost:8080/
[[boot-explore]]
=== Exploring the security Sample Application
Try using the application. Enter the following to log in:
@@ -112,6 +114,7 @@ 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 Redis rather than Tomcat's `HttpSession` implementation.
[[boot-how]]
=== How does it work?
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
@@ -123,12 +126,12 @@ Go ahead and view the cookies (click for help with https://developer.chrome.com/
If you like, you can easily remove the session using redis-cli. For example, on a Linux based system you can type:
$ redis-cli keys '*' | xargs redis-cli del
$ redis-cli keys '*' | xargs redis-cli del
TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli].
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.

View File

@@ -32,7 +32,21 @@ For example, the configuration might look something like the following:
include::{websocketdoc-test-dir}WebSocketConfig.java[tags=class]
----
To hook in the Spring Session support, we need to ensure
We can easily update our configuration to use Spring Session's WebSocket support.
For example:
.src/main/java/samples/config/WebSocketConfig.java
[source,java]
----
include::{samples-dir}boot/websocket/src/main/java/sample/config/WebSocketConfig.java[tags=class]
----
To hook in the Spring Session support we only need to change two things:
<1> Instead of extending `AbstractWebSocketMessageBrokerConfigurer` we extend `AbstractSessionWebSocketMessageBrokerConfigurer`
<2> We rename the `registerStompEndpoints` method to `configureStompEndpoints`
What does `AbstractSessionWebSocketMessageBrokerConfigurer` do behind the scenes?
* `WebSocketConnectHandlerDecoratorFactory` is added as a `WebSocketHandlerDecoratorFactory` to `WebSocketTransportRegistration`.
This ensures a custom `SessionConnectEvent` is fired that contains the `WebSocketSession`.
@@ -45,20 +59,6 @@ This ensures that every time an inbound message is received, that the last acces
This ensures that we have a mapping of all of the Session id to the corresponding WebSocket connections.
By maintaining this mapping, we can close all the WebSocket connections when a Spring Session (HttpSession) is terminated.
This is quite a bit of work to get things working.
Fortunately, Spring Session provides a convenience class named `AbstractSessionWebSocketMessageBrokerConfigurer` that hides the complexity of the configuration.
An example is provided below:
.src/main/java/samples/config/WebSocketConfig.java
[source,java]
----
include::{samples-dir}websocket/src/main/java/sample/config/WebSocketConfig.java[tags=class]
----
We only need to change two things to use Spring Session:
<1> Instead of extending `AbstractWebSocketMessageBrokerConfigurer` we extend `AbstractSessionWebSocketMessageBrokerConfigurer`
<2> We rename the `registerStompEndpoints` method to `configureStompEndpoints`
// end::config[]
@@ -73,16 +73,24 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
[TIP]
====
For the purposes of testing session expiration, you may want to change the session expiration to be 1 minute (default is 30 minutes) by removing the comment from the following file before starting the application:
For the purposes of testing session expiration, you may want to change the session expiration to be 1 minute (default is 30 minutes) by adding the following configuration property starting before the application:
.src/main/java/samples/config/WebSecurityConfig.java
[source,java]
.src/main/resources/application.properties
----
include::{samples-dir}websocket/src/main/java/sample/config/WebSecurityConfig.java[tags=enable-redis-httpsession]
server.session.timeout=60
----
====
$ ./gradlew :samples:websocket:bootRun
[NOTE]
====
For the sample to work, you must http://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.
====
----
$ ./gradlew :samples:websocket:bootRun
----
You should now be able to access the application at http://localhost:8080/
@@ -127,4 +135,4 @@ This demonstrates the session is kept alive.
NOTE: Only messages sent from a user keep the session alive.
This is because only messages coming from a user imply user activity.
Messages received do not imply activity and thus do not renew the session expiration.
Messages received do not imply activity and thus do not renew the session expiration.

View File

@@ -0,0 +1,133 @@
= Spring Session - Grails
Eric Helgeson
:toc:
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using Grails 3.1
NOTE: Grails 3.1 is based off spring boot 1.3 so much of the advanced configuration and options can be found in the boot docs as well.
NOTE: The completed guide can be found in the <<grails3-sample, Grails 3 sample application>>.
== Updating Dependencies
Before you use Spring Session, you must ensure to update your dependencies.
We assume you are working with a working Grails 3.1 web profile.
Add the following dependencies:
.build.gradle
[source,groovy]
[subs="verbatim,attributes"]
----
dependencies {
compile 'org.springframework.boot:spring-boot-starter-redis'
compile 'org.springframework.session:spring-session:{spring-session-version}'
}
----
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:
.build.gradle
[source,groovy]
----
repositories {
maven {
url '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:
.build.gradle
[source,groovy]
----
repositories {
maven {
url 'https://repo.spring.io/libs-milestone'
}
}
----
endif::[]
[[grails3-redis-configuration]]
== Configuring the Redis Connection
Spring Boot automatically creates a `RedisConnectionFactory` that connects Spring Session to a Redis Server on localhost on port 6379 (default port).
In a production environment you need to ensure to update your configuration to point to your Redis server.
For example, you can include the following in your *application.yml*
.grails-app/conf/application.yml
[source,yml]
----
spring:
redis:
host: localhost
password: secret
port: 6397
----
For more information, refer to http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-connecting-to-redis[Connecting to Redis] portion of the Spring Boot documentation.
[[grails3-sample]]
== Grails 3 Sample Application
The Grails 3 Sample Application demonstrates how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using Grails.
[[grails3-running]]
=== Running the Grails 3 Sample Application
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
[NOTE]
====
For the sample to work, you must http://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.
====
----
$ ./gradlew :samples:grails3:bootRun
----
You should now be able to access the application at http://localhost:8080/test/index
[[grails3-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 Redis rather than Tomcat's `HttpSession` implementation.
[[grails3-how]]
=== How does it work?
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
Spring Session replaces the `HttpSession` with an implementation that is backed by Redis.
When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into Redis.
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 redis-cli. For example, on a Linux based system you can type:
$ redis-cli keys '*' | xargs redis-cli del
TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli].
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
Now visit the application at http://localhost:8080/test/index and observe that we are no longer authenticated.
NOTE: Spring Session will not work with grails flash scope without additional work. +
See this answer for an explanation: https://stackoverflow.com/a/43311427

View File

@@ -0,0 +1,102 @@
= Spring Session - Custom Cookie
Rob Winch
:toc:
This guide describes how to configure Spring Session to use custom cookies with Java Configuration.
The guide assumes you have already link:./httpsession.html[setup Spring Session in your project].
NOTE: The completed guide can be found in the <<custom-cookie-sample, Custom Cookie sample application>>.
[[custom-cookie-spring-configuration]]
== Spring Java Configuration
Once you have setup Spring Session you can easily customize how the session cookie is written by exposing a `CookieSerializer` as a Spring Bean.
Out of the box, Spring Session comes with `DefaultCookieSerializer`.
Simply exposing the `DefaultCookieSerializer` as a Spring Bean will augment the existing configuration when using configurations like `@EnableRedisHttpSession`.
You can find an example of customizing Spring Session's cookie below:
[source,java]
----
include::{samples-dir}javaconfig/custom-cookie/src/main/java/sample/Config.java[tags=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 domain name pattern (a regular expression) to be `^.+?\\.(\\w+\\.[a-z]+)$`
This allows sharing a session across domains and applications.
If the regular expression does not match, no domain is set and the existing domain will be used.
If the regular expression matches, the first https://docs.oracle.com/javase/tutorial/essential/regex/groups.html[grouping] will be used as the domain.
This means that a request to https://child.example.com will set the domain to example.com.
However, a request to http://localhost:8080/ or http://192.168.1.100:8080/ will leave the cookie unset and thus still work in development without any changes necessary for production.
[WARNING]
====
It is important to note that users should only match on valid domain characters since the domain name is reflected in the response.
This is prevent a malicious user from performing attacks like https://en.wikipedia.org/wiki/HTTP_response_splitting[HTTP Response Splitting].
====
[[custom-cookie-options]]
== Configuration Options
The configuration options available are:
* `cookieName` - the name of the cookie to use
Default "SESSION"
* `useSecureCookie` - specify if a secure cookie be used
Default use value of `HttpServletRequest.isSecure()` at the time of creation.
* `cookiePath` - the path of the cookie
Default is context root
* `cookieMaxAge` - specifies the max age of the cookie to be set at the time the session is created.
Default is -1 which indicates the cookie will be removed when the browser is closed.
* `jvmRoute` - specifies a suffix to be appended to the session id and included in the cookie.
Used to identify which JVM to route to for session affinity.
With some implementations (i.e. Redis) this provides no performance benefit.
However, this can help with tracing logs of a particular user.
* `domainName` - allows specifying a specific domain name to be used for the cookie.
This option is simple to understand, but will likely require a different configuration between development and production environments.
See domainNamePattern as an alternative.
* `domainNamePattern` - a case insensitive pattern used to extract the domain name from the `HttpServletRequest#getServerName()`.
The pattern should provide a single grouping used to extract the value of the cookie domain.
If the regular expression does not match, no domain is set and the existing domain will be used.
If the regular expression matches, the first https://docs.oracle.com/javase/tutorial/essential/regex/groups.html[grouping] will be used as the domain.
[WARNING]
====
It is important to note that users should only match on valid domain characters since the domain name is reflected in the response.
This is prevent a malicious user from performing attacks like https://en.wikipedia.org/wiki/HTTP_response_splitting[HTTP Response Splitting].
====
[[custom-cookie-sample]]
== custom-cookie Sample Application
=== Running the custom-cookie Sample Application
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
[NOTE]
====
For the sample to work, you must http://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.
====
----
$ ./gradlew :samples:custom-cookie:tomcatRun
----
You should now be able to access the application at http://localhost:8080/
=== Exploring the custom-cookie Sample Application
Try using 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

@@ -0,0 +1,195 @@
= Spring Session and Spring Security with Hazelcast
Tommy Ludwig; Rob Winch
:toc:
This guide describes how to use Spring Session along with Spring Security using Hazelcast as your data store.
It assumes you have already applied Spring Security to your application.
NOTE: The completed guide can be found in the <<hazelcast-spring-security-sample, Hazelcast Spring Security sample application>>.
== Updating Dependencies
Before you use Spring Session, you must ensure to update your dependencies.
If you are using Maven, ensure to add the following dependencies:
.pom.xml
[source,xml]
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>{hazelcast-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
</dependencies>
----
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]
----
<repositories>
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
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]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
// tag::config[]
[[security-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::{docs-test-dir}docs/http/HazelcastHttpSessionConfig.java[tags=config]
----
<1> The `@EnableHazelcastHttpSession` 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 Hazelcast.
<2> In order to support retrieval of sessions by principal name index, appropriate `ValueExtractor` needs to be registered.
Spring Session provides `PrincipalNameExtractor` for this purpose.
<3> We create a `HazelcastInstance` that connects Spring Session to Hazelcast.
By default, an embedded instance of Hazelcast is started and connected to by the application.
For more information on configuring Hazelcast, refer to the http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[reference documentation].
== Servlet Container Initialization
Our <<security-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 `SessionConfig` class.
Since our application is already loading Spring configuration using our `SecurityInitializer` class, we can simply add our `SessionConfig` class to it.
.src/main/java/sample/SecurityInitializer.java
[source,java]
----
include::{samples-dir}javaconfig/hazelcast/src/main/java/sample/SecurityInitializer.java[tags=class]
----
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
It is extremely important that Spring Session's `springSessionRepositoryFilter` is invoked before Spring Security's `springSecurityFilterChain`.
This ensures that the `HttpSession` that Spring Security uses is backed by Spring Session.
Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` that makes this extremely easy.
You can find an example below:
.src/main/java/sample/Initializer.java
[source,java]
----
include::{samples-dir}javaconfig/hazelcast/src/main/java/sample/Initializer.java[tags=class]
----
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
By extending `AbstractHttpSessionApplicationInitializer` we ensure that the Spring Bean by the name `springSessionRepositoryFilter` is registered with our Servlet Container for every request before Spring Security's `springSecurityFilterChain`.
// end::config[]
[[hazelcast-spring-security-sample]]
== Hazelcast Spring Security Sample Application
=== Running the Sample Application
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
[NOTE]
====
Hazelcast will run in embedded mode with your application by default, but if you want to connect
to a stand alone instance instead, you can configure it by following the instructions in the
http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[reference documentation].
====
----
$ ./gradlew :samples:hazelcast-spring:tomcatRun
----
You should now be able to access the application at http://localhost:8080/
=== 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 Hazelcast rather than Tomcat's `HttpSession` implementation.
=== How does it work?
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Hazelcast.
Spring Session replaces the `HttpSession` with an implementation that is backed by a `Map` in Hazelcast.
When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into Hazelcast.
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]).
=== Interact with the data store
If you like, you can remove the session using http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-java-client[a Java client],
http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#other-client-implementations[one of the other clients], or the
http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#management-center[management center].
==== Using the console
For example, using the management center console after connecting to your Hazelcast node:
default> ns spring:session:sessions
spring:session:sessions> m.clear
TIP: The Hazelcast documentation has instructions for http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#executing-console-commands[the console].
Alternatively, you can also delete the explicit key. Enter the following into the console ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
spring:session:sessions> m.remove 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.
==== Using the REST API
As described in the other clients section of the documentation, there is a
http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#rest-client[REST API]
provided by the Hazelcast node(s).
For example, you could delete an individual key as follows (replacing `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie):
$ curl -v -X DELETE http://localhost:xxxxx/hazelcast/rest/maps/spring:session:sessions/7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
TIP: The port number of the Hazelcast node will be printed to the console on startup. Replace `xxxxx` above with the port number.
Now observe that you are no longer authenticated with this session.

View File

@@ -0,0 +1,152 @@
= Spring Session - HttpSession (Quick Start)
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` with Java Configuration.
NOTE: The completed guide can be found in the <<httpsession-jdbc-sample, httpsession-jdbc sample application>>.
== Updating Dependencies
Before you use Spring Session, you must ensure to update your dependencies.
If you are using Maven, ensure to add the following dependencies:
.pom.xml
[source,xml]
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
<version>{spring-session-version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
</dependencies>
----
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]
----
<repositories>
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
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]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
// tag::config[]
[[httpsession-jdbc-spring-configuration]]
== Spring Java 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}javaconfig/jdbc/src/main/java/sample/Config.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.
<2> We create a `dataSource` that connects Spring Session to an embedded instance of H2 database.
We configure the H2 database to create database tables using the SQL script which is included in Spring Session.
<3> We create a `transactionManager` that manages transactions for previously configured `dataSource`.
For additional information on how to configure data access related concerns, please refer to the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html[Spring Framework Reference Documentation].
== Java Servlet Container Initialization
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.
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` both of these steps extremely easy.
You can find an example below:
.src/main/java/sample/Initializer.java
[source,java]
----
include::{samples-dir}javaconfig/jdbc/src/main/java/sample/Initializer.java[tags=class]
----
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`.
This ensures that the Spring Bean by the name `springSessionRepositoryFilter` is registered with our Servlet Container for every request.
<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily ensure Spring loads our `Config`.
// end::config[]
[[httpsession-jdbc-sample]]
== httpsession-jdbc Sample Application
=== Running the httpsession-jdbc Sample Application
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
----
$ ./gradlew :samples:httpsession-jdbc:tomcatRun
----
You should now be able to access the application at http://localhost:8080/
=== Exploring the httpsession-jdbc Sample Application
Try using 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.
=== How does it work?
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
.src/main/java/sample/SessionServlet.java
[source,java]
----
include::{samples-dir}javaconfig/jdbc/src/main/java/sample/SessionServlet.java[tags=class]
----
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in H2 database.
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 the attribute we added is no longer displayed.

View File

@@ -2,7 +2,7 @@
Rob Winch
:toc:
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession`.
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` with Java Configuration.
NOTE: The completed guide can be found in the <<httpsession-sample, httpsession sample application>>.
@@ -15,24 +15,29 @@ If you are using Maven, ensure to add the following dependencies:
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>{lettuce-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
</dependencies>
----
ifeval::["{version-snapshot}" == "true"]
Since We are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
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
@@ -40,12 +45,12 @@ Ensure you have the following in your pom.xml:
----
<repositories>
<!-- ... -->
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
endif::[]
@@ -58,8 +63,8 @@ Ensure you have the following in your pom.xml:
[source,xml]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
@@ -67,7 +72,7 @@ endif::[]
// tag::config[]
[[httpsession-spring-configuration]]
== Spring Configuration
== Spring Java 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.
@@ -75,19 +80,17 @@ Add the following Spring Configuration:
[source,java]
----
include::{samples-dir}httpsession/src/main/java/sample/Config.java[]
include::{samples-dir}javaconfig/redis/src/main/java/sample/Config.java[tags=class]
----
<1> We import an embedded Redis Server so that there is no need to start up Redis external of our application.
In a production application this is not necessary since we would point our connection to an external Redis instance.
<2> The `@EnableRedisHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
<1> The `@EnableRedisHttpSession` 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 Redis.
<3> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
In our example, we are connecting to localhost on the default port (6379).
<2> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
We configure the connection to connect to localhost on the default port (6379)
For more information on configuring Spring Data Redis, refer to the http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
== Servlet Container Initialization
== Java Servlet Container Initialization
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.
@@ -100,7 +103,7 @@ You can find an example below:
.src/main/java/sample/Initializer.java
[source,java]
----
include::{samples-dir}httpsession/src/main/java/sample/Initializer.java[]
include::{samples-dir}javaconfig/redis/src/main/java/sample/Initializer.java[tags=class]
----
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
@@ -120,7 +123,16 @@ This ensures that the Spring Bean by the name `springSessionRepositoryFilter` is
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
$ ./gradlew :samples:httpsession:tomcatRun
[NOTE]
====
For the sample to work, you must http://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.
====
----
$ ./gradlew :samples:httpsession:tomcatRun
----
You should now be able to access the application at http://localhost:8080/
@@ -128,8 +140,8 @@ You should now be able to access the application at http://localhost:8080/
Try using the application. Fill out the form with the following information:
* **Attribute Name** *username*
* **Attribute Value** *rob*
* **Attribute Name:** _username_
* **Attribute Value:** _rob_
Now click the **Set Attribute** button. You should now see the values displayed in the table.
@@ -140,7 +152,7 @@ We interact with the standard `HttpSession` in the `SessionServlet` shown below:
.src/main/java/sample/SessionServlet.java
[source,java]
----
include::{samples-dir}httpsession/src/main/java/sample/SessionServlet.java[]
include::{samples-dir}javaconfig/redis/src/main/java/sample/SessionServlet.java[tags=class]
----
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
@@ -149,12 +161,12 @@ Go ahead and view the cookies (click for help with https://developer.chrome.com/
If you like, you can easily remove the session using redis-cli. For example, on a Linux based system you can type:
$ redis-cli keys '*' | xargs redis-cli del
$ redis-cli keys '*' | xargs redis-cli del
TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli].
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.

View File

@@ -15,19 +15,24 @@ If you are using Maven, ensure to add the following dependencies:
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>{lettuce-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
</dependencies>
----
@@ -40,12 +45,12 @@ Ensure you have the following in your pom.xml:
----
<repositories>
<!-- ... -->
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
endif::[]
@@ -58,8 +63,8 @@ Ensure you have the following in your pom.xml:
[source,xml]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
@@ -75,18 +80,16 @@ Add the following Spring Configuration:
[source,java]
----
include::{samples-dir}rest/src/main/java/sample/HttpSessionConfig.java[]
include::{samples-dir}javaconfig/rest/src/main/java/sample/HttpSessionConfig.java[tags=class]
----
<1> We import an embedded Redis Server so that there is no need to start up Redis external of our application.
In a production application this is not necessary since we would point our connection to an external Redis instance.
<2> The `@EnableRedisHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
<1> The `@EnableRedisHttpSession` 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 Redis.
<3> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
In our example, we are connecting to localhost on the default port (6379).
<2> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
We configure the connection to connect to localhost on the default port (6379)
For more information on configuring Spring Data Redis, refer to the http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
<4> We customize Spring Session's HttpSession integration to use HTTP headers to convey the current session information instead of cookies.
<3> We customize Spring Session's HttpSession integration to use HTTP headers to convey the current session information instead of cookies.
== Servlet Container Initialization
@@ -98,7 +101,7 @@ In order for our `Filter` to do its magic, Spring needs to load our `Config` cla
.src/main/java/sample/mvc/MvcInitializer.java
[source,java,indent=0]
----
include::{samples-dir}rest/src/main/java/sample/mvc/MvcInitializer.java[tags=config]
include::{samples-dir}javaconfig/rest/src/main/java/sample/mvc/MvcInitializer.java[tags=config]
----
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
@@ -107,7 +110,7 @@ Fortunately, Spring Session provides a utility class named `AbstractHttpSessionA
.src/main/java/sample/Initializer.java
[source,java]
----
include::{samples-dir}rest/src/main/java/sample/Initializer.java[]
include::{samples-dir}javaconfig/rest/src/main/java/sample/Initializer.java[tags=class]
----
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
@@ -121,7 +124,16 @@ NOTE: The name of our class (Initializer) does not matter. What is important is
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
$ ./gradlew :samples:rest:tomcatRun
[NOTE]
====
For the sample to work, you must http://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.
====
----
$ ./gradlew :samples:rest:tomcatRun
----
You should now be able to access the application at http://localhost:8080/
@@ -129,21 +141,21 @@ You should now be able to access the application at http://localhost:8080/
Try using the application. Use your favorite REST client to request http://localhost:8080/
$ curl -v http://localhost:8080/
$ curl -v http://localhost:8080/
Observe that we are prompted for basic authentication. Provide the following information for the username and password:
* **Username** *user*
* **Password** *password*
$ curl -v http://localhost:8080/ -u user:password
$ curl -v http://localhost:8080/ -u user:password
In the output you will notice the following:
----
HTTP/1.1 200 OK
...
x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3
X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3
{"username":"user"}
----
@@ -151,58 +163,58 @@ x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3
Specifically, we notice the following things about our response:
* The HTTP Status is now a 200
* We have a header with the name of *x-auth-token* which contains a new session id
* We have a header with the name of *X-Auth-Token* which contains a new session id
* The current username is displayed
We can now use the *x-auth-token* to make another request without providing the username and password again. For example, the following outputs the the username just as before:
We can now use the *X-Auth-Token* to make another request without providing the username and password again. For example, the following outputs the username just as before:
$ curl -v http://localhost:8080/ -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
$ curl -v http://localhost:8080/ -H "X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
The only difference is that the session id is not provided in the response headers because we are reusing an existing session.
If we invalidate the session, then the x-auth-token is displayed in the response with an empty value. For example, the following will invalidate our session:
If we invalidate the session, then the X-Auth-Token is displayed in the response with an empty value. For example, the following will invalidate our session:
$ curl -v http://localhost:8080/logout -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
$ curl -v http://localhost:8080/logout -H "X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
You will see in the output that the x-auth-token provides an empty String indicating that the previous session was invalidated.
You will see in the output that the X-Auth-Token provides an empty String indicating that the previous session was invalidated.
----
HTTP/1.1 204 No Content
...
x-auth-token:
---
X-Auth-Token:
----
=== How does it work?
Spring Security interacts with the standard `HttpSession` in `SecurityContextPersistenceFilter`.
Instead of using Tomcat's `HttpSession`, Spring Security is now persisting the values in Redis.
Spring Session creates a header named x-auth-token in your browser that contains the id of your session.
Spring Session creates a header named X-Auth-Token in your browser that contains the id of your session.
If you like, you can easily see that the session is created in Redis. First create a session using the following:
$ curl -v http://localhost:8080/ -u user:password
$ curl -v http://localhost:8080/ -u user:password
In the output you will notice the following:
----
HTTP/1.1 200 OK
...
x-auth-token: 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
X-Auth-Token: 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
{"username":"user"}
----
Now remove the session using redis-cli. For example, on a Linux based system you can type:
$ redis-cli keys '*' | xargs redis-cli del
$ redis-cli keys '*' | xargs redis-cli del
TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli].
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
We can now use the *x-auth-token* to make another request with the session we deleted and observe we are prompted for a authentication. For example, the following returns an HTTP 401:
We can now use the *X-Auth-Token* to make another request with the session we deleted and observe we are prompted for a authentication. For example, the following returns an HTTP 401:
$ curl -v http://localhost:8080/ -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
$ curl -v http://localhost:8080/ -H "X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"

View File

@@ -16,19 +16,24 @@ If you are using Maven, ensure to add the following dependencies:
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>{lettuce-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
</dependencies>
----
@@ -41,12 +46,12 @@ Ensure you have the following in your pom.xml:
----
<repositories>
<!-- ... -->
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
endif::[]
@@ -59,8 +64,8 @@ Ensure you have the following in your pom.xml:
[source,xml]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
@@ -74,16 +79,14 @@ Add the following Spring Configuration:
[source,java]
----
include::{samples-dir}security/src/main/java/sample/Config.java[]
include::{samples-dir}javaconfig/security/src/main/java/sample/Config.java[tags=class]
----
<1> We import an embedded Redis Server so that there is no need to start up Redis external of our application.
In a production application this is not necessary since we would point our connection to an external Redis instance.
<2> The `@EnableRedisHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
<1> The `@EnableRedisHttpSession` 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 Redis.
<3> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
In our example, we are connecting to localhost on the default port (6379).
<2> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
We configure the connection to connect to localhost on the default port (6379)
For more information on configuring Spring Data Redis, refer to the http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
== Servlet Container Initialization
@@ -97,7 +100,7 @@ Since our application is already loading Spring configuration using our `Securit
.src/main/java/sample/SecurityInitializer.java
[source,java]
----
include::{samples-dir}security/src/main/java/sample/SecurityInitializer.java[]
include::{samples-dir}javaconfig/security/src/main/java/sample/SecurityInitializer.java[tags=class]
----
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
@@ -109,7 +112,7 @@ You can find an example below:
.src/main/java/sample/Initializer.java
[source,java]
----
include::{samples-dir}security/src/main/java/sample/Initializer.java[]
include::{samples-dir}javaconfig/security/src/main/java/sample/Initializer.java[tags=class]
----
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
@@ -125,7 +128,16 @@ By extending `AbstractHttpSessionApplicationInitializer` we ensure that the Spri
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
$ ./gradlew :samples:security:tomcatRun
[NOTE]
====
For the sample to work, you must http://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.
====
----
$ ./gradlew :samples:security:tomcatRun
----
You should now be able to access the application at http://localhost:8080/
@@ -151,12 +163,12 @@ Go ahead and view the cookies (click for help with https://developer.chrome.com/
If you like, you can easily remove the session using redis-cli. For example, on a Linux based system you can type:
$ redis-cli keys '*' | xargs redis-cli del
$ redis-cli keys '*' | xargs redis-cli del
TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli].
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.

View File

@@ -17,7 +17,16 @@ The users application demonstrates how to allow an application to manage multipl
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
$ ./gradlew :samples:users:tomcatRun
[NOTE]
====
For the sample to work, you must http://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.
====
----
$ ./gradlew :samples:users:tomcatRun
----
You should now be able to access the application at http://localhost:8080/
@@ -71,7 +80,7 @@ Let's take a look at how Spring Session keeps track of multiple sessions.
Spring Session keeps track of the `HttpSession` by adding a value to a cookie named SESSION.
For example, the SESSION cookie might have a value of:
7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
=== Adding a Session
@@ -91,7 +100,7 @@ We can obtain the `HttpSessionManager` from the `HttpServletRequest` using the f
.src/main/java/sample/UserAccountsFilter.java
[source,java,indent=0]
----
include::{samples-dir}users/src/main/java/sample/UserAccountsFilter.java[tags=HttpSessionManager]
include::{samples-dir}javaconfig/users/src/main/java/sample/UserAccountsFilter.java[tags=HttpSessionManager]
----
We can now use it to create a URL to add another session.
@@ -99,21 +108,21 @@ We can now use it to create a URL to add another session.
.src/main/java/sample/UserAccountsFilter.java
[source,java,indent=0]
----
include::{samples-dir}users/src/main/java/sample/UserAccountsFilter.java[tags=addAccountUrl]
include::{samples-dir}javaconfig/users/src/main/java/sample/UserAccountsFilter.java[tags=addAccountUrl]
----
<1> We have an existing variable named `unauthenticatedAlias`.
The value is an alias that points to existing unauthenticated session.
The value is an alias that points to an existing unauthenticated session.
If no such session exists, the value is null.
This ensures if we have an existing unauthenticated session that we use it instead of creating a new session.
<2> If all of our sessions are already associated to a user, we create a new session alias.
<3> If a there exists a session that is not associated to a user, we use its session alias.
<3> If there is an existing session that is not associated to a user, we use its session alias.
<4> Finally, we create the add account URL.
The URL contains a session alias that either points to an existing unauthenticated session or is an alias that is unused thus signaling to create a new session associated to that alias.
Now our SESSION cookie looks something like this:
0 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e 1 1d526d4a-c462-45a4-93d9-84a39b6d44ad
0 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e 1 1d526d4a-c462-45a4-93d9-84a39b6d44ad
Such that:
@@ -140,7 +149,7 @@ For example, if we are currently using the session with the alias of *1*, then t
.src/main/webapp/index.jsp
[source,xml,indent=0]
----
include::{samples-dir}users/src/main/webapp/index.jsp[tags=link]
include::{samples-dir}javaconfig/users/src/main/webapp/index.jsp[tags=link]
----
will output a link of:
@@ -150,4 +159,4 @@ will output a link of:
<a id="navLink" href="/link.jsp?_s=1">Link</a>
----
end::how-does-it-work[]
// end::how-does-it-work[]

View File

@@ -0,0 +1,162 @@
= Spring Session - HttpSession (Quick Start)
Rob Winch, Vedran Pavić
: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: The completed guide can be found in the <<httpsession-jdbc-xml-sample, httpsession-jdbc-xml sample application>>.
== Updating Dependencies
Before you use Spring Session, you must ensure to update your dependencies.
If you are using Maven, ensure to add the following dependencies:
.pom.xml
[source,xml]
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
<version>{spring-session-version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
</dependencies>
----
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]
----
<repositories>
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
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]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
// tag::config[]
[[httpsession-jdbc-xml-spring-configuration]]
== Spring XML 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:
.src/main/webapp/WEB-INF/spring/session.xml
[source,xml,indent=0]
----
include::{samples-dir}xml/jdbc/src/main/webapp/WEB-INF/spring/session.xml[tags=beans]
----
<1> We use the combination of `<context:annotation-config/>` and `JdbcHttpSessionConfiguration` because Spring Session does not yet provide XML Namespace support (see https://github.com/spring-projects/spring-session/issues/104[gh-104]).
This 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.
<2> We create a `dataSource` that connects Spring Session to an embedded instance of H2 database.
We configure the H2 database to create database tables using the SQL script which is included in Spring Session.
<3> We create a `transactionManager` that manages transactions for previously configured `dataSource`.
For additional information on how to configure data access related concerns, please refer to the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html[Spring Framework Reference Documentation].
== XML Servlet Container Initialization
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.
We do this with the following configuration:
.src/main/webapp/WEB-INF/web.xml
[source,xml,indent=0]
----
include::{samples-dir}xml/jdbc/src/main/webapp/WEB-INF/web.xml[tags=context-param]
include::{samples-dir}xml/jdbc/src/main/webapp/WEB-INF/web.xml[tags=listeners]
----
The http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener] reads the contextConfigLocation and picks up our session.xml configuration.
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
The following snippet performs this last step for us:
.src/main/webapp/WEB-INF/web.xml
[source,xml,indent=0]
----
include::{samples-dir}xml/jdbc/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter]
----
The http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy] will look up a Bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`.
For every request that `DelegatingFilterProxy` is invoked, the `springSessionRepositoryFilter` will be invoked.
// end::config[]
[[httpsession-jdbc-xml-sample]]
== httpsession-jdbc-xml Sample Application
=== Running the httpsession-jdbc-xml Sample Application
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
----
$ ./gradlew :samples:httpsession-jdbc-xml:tomcatRun
----
You should now be able to access the application at http://localhost:8080/
=== Exploring the httpsession-jdbc-xml Sample Application
Try using 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.
=== How does it work?
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
.src/main/java/sample/SessionServlet.java
[source,java]
----
include::{samples-dir}xml/jdbc/src/main/java/sample/SessionServlet.java[tags=class]
----
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in H2 database.
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 the attribute we added is no longer displayed.

View File

@@ -0,0 +1,180 @@
= Spring Session - HttpSession (Quick Start)
Rob Winch
: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: The completed guide can be found in the <<httpsession-xml-sample, httpsession-xml sample application>>.
== Updating Dependencies
Before you use Spring Session, you must ensure to update your dependencies.
If you are using Maven, ensure to add the following dependencies:
.pom.xml
[source,xml]
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>{lettuce-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
</dependencies>
----
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]
----
<repositories>
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
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]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
// tag::config[]
[[httpsession-xml-spring-configuration]]
== Spring XML 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:
.src/main/webapp/WEB-INF/spring/session.xml
[source,xml,indent=0]
----
include::{samples-dir}xml/redis/src/main/webapp/WEB-INF/spring/session.xml[tags=beans]
----
<1> We use the combination of `<context:annotation-config/>` and `RedisHttpSessionConfiguration` because Spring Session does not yet provide XML Namespace support (see https://github.com/spring-projects/spring-session/issues/104[gh-104]).
This 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 Redis.
<2> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
We configure the connection to connect to localhost on the default port (6379)
For more information on configuring Spring Data Redis, refer to the http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
== XML Servlet Container Initialization
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.
We do this with the following configuration:
.src/main/webapp/WEB-INF/web.xml
[source,xml,indent=0]
----
include::{samples-dir}xml/redis/src/main/webapp/WEB-INF/web.xml[tags=context-param]
include::{samples-dir}xml/redis/src/main/webapp/WEB-INF/web.xml[tags=listeners]
----
The http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener] reads the contextConfigLocation and picks up our session.xml configuration.
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
The following snippet performs this last step for us:
.src/main/webapp/WEB-INF/web.xml
[source,xml,indent=0]
----
include::{samples-dir}xml/redis/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter]
----
The http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy] will look up a Bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`.
For every request that `DelegatingFilterProxy` is invoked, the `springSessionRepositoryFilter` will be invoked.
// end::config[]
[[httpsession-xml-sample]]
== httpsession-xml Sample Application
=== Running the httpsession-xml Sample Application
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
[NOTE]
====
For the sample to work, you must http://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.
====
----
$ ./gradlew :samples:httpsession-xml:tomcatRun
----
You should now be able to access the application at http://localhost:8080/
=== Exploring the httpsession-xml Sample Application
Try using 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.
=== How does it work?
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
.src/main/java/sample/SessionServlet.java
[source,java]
----
include::{samples-dir}xml/redis/src/main/java/sample/SessionServlet.java[tags=class]
----
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
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 redis-cli. For example, on a Linux based system you can type:
$ redis-cli keys '*' | xargs redis-cli del
TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli].
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.

View File

@@ -1,5 +1,6 @@
= Spring Session
Rob Winch
Rob Winch, Vedran Pavić, Jakub Kubrynski
:doctype: book
:indexdoc-tests: {docs-test-dir}docs/IndexDocTests.java
:websocketdoc-test-dir: {docs-test-dir}docs/websocket/
@@ -22,42 +23,111 @@ Additional features include:
* <<websocket,WebSocket>> - provides the ability to keep the `HttpSession` alive when receiving WebSocket messages
== What's New in 1.3
Below are the highlights of what is new in Spring Session 1.3. You can find a complete list of what's new by referring to the changelogs of
https://github.com/spring-projects/spring-session/milestone/6?closed=1[1.3.0.M1],
https://github.com/spring-projects/spring-session/milestone/18?closed=1[1.3.0.M2],
https://github.com/spring-projects/spring-session/milestone/16?closed=1[1.3.0.RC1], and
https://github.com/spring-projects/spring-session/milestone/19?closed=1[1.3.0.RELEASE].
* First class support for http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/#httpsession-hazelcast[Hazelcast]
* First class support for http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/#spring-security-concurrent-sessions-how[Spring Security's concurrent session management]
* Added https://github.com/maseev/spring-session-orientdb[OrientDB Community Extension]
* https://github.com/spring-projects/spring-session/tree/1.3.0.RELEASE/samples/httpsession-redis-json[GenericJackson2JsonRedisSerializer sample] with Spring Security's new Jackson Support
* Guides now https://github.com/spring-projects/spring-session/pull/652[use Lettuce]
* `spring.session.cleanup.cron.expression` can be used to override the cleanup tasks cron expression
* Lots of performance improvements and bug fixes
[[samples]]
== Samples and Guides (Start Here)
If you are looking to get started with Spring Session, the best place to start is our Sample Applications.
.Sample Applications
.Sample Applications using Spring Boot
|===
| Source | Description | Guide
| {gh-samples-url}httpsession[HttpSession]
| Demonstrates how to use Spring Session to replace the `HttpSession` with a Redis store.
| link:guides/httpsession.html[HttpSession Guide]
| {gh-samples-url}boot/redis[HttpSession with Redis]
| Demonstrates how to use Spring Session to replace the `HttpSession` with Redis.
| link:guides/boot-redis.html[HttpSession with Redis Guide]
| {gh-samples-url}boot[Spring Boot]
| Demonstrates how to use Spring Session with Spring Boot.
| link:guides/boot.html[Spring Boot Guide]
| {gh-samples-url}boot/jdbc[HttpSession with JDBC]
| Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store.
| link:guides/boot-jdbc.html[HttpSession with JDBC Guide]
| {gh-samples-url}security[Spring Security]
| Demonstrates how to use Spring Session with an existing Spring Security application.
| link:guides/security.html[Spring Security Guide]
| {gh-samples-url}boot/findbyusername[Find by Username]
| Demonstrates how to use Spring Session to find sessions by username.
| link:guides/boot-findbyusername.html[Find by Username Guide]
| {gh-samples-url}rest[REST]
| Demonstrates how to use Spring Session in a REST application to support authenticating with a header.
| link:guides/rest.html[REST Guide]
| {gh-samples-url}users[Multiple Users]
| Demonstrates how to use Spring Session to manage multiple simultaneous browser sessions (i.e Google Accounts).
| link:guides/users.html[Manage Multiple Users Guide]
| {gh-samples-url}websocket[WebSocket]
| {gh-samples-url}boot/websocket[WebSockets]
| Demonstrates how to use Spring Session with WebSockets.
| link:guides/websocket.html[WebSocket Guide]
| link:guides/boot-websocket.html[WebSockets Guide]
[[samples-hazelcast]]
| {gh-samples-url}hazelcast[Hazelcast]
| Demonstrates how to use Spring Session with Hazelcast.
| {gh-samples-url}boot/redis-json[HttpSession with Redis JSON serialization]
| Demonstrates how to use Spring Session to replace the `HttpSession` with Redis using JSON serialization.
| TBD
|===
.Sample Applications using Spring Java based configuration
|===
| Source | Description | Guide
| {gh-samples-url}javaconfig/redis[HttpSession with Redis]
| Demonstrates how to use Spring Session to replace the `HttpSession` with Redis.
| link:guides/java-redis.html[HttpSession with Redis Guide]
| {gh-samples-url}javaconfig/jdbc[HttpSession with JDBC]
| Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store.
| link:guides/java-jdbc.html[HttpSession with JDBC Guide]
| {gh-samples-url}javaconfig/hazelcast[HttpSession with Hazelcast]
| Demonstrates how to use Spring Session to replace the `HttpSession` with Hazelcast.
| link:guides/java-hazelcast.html[HttpSession with Hazelcast Guide]
| {gh-samples-url}javaconfig/custom-cookie[Custom Cookie]
| Demonstrates how to use Spring Session and customize the cookie.
| link:guides/java-custom-cookie.html[Custom Cookie Guide]
| {gh-samples-url}javaconfig/security[Spring Security]
| Demonstrates how to use Spring Session with an existing Spring Security application.
| link:guides/java-security.html[Spring Security Guide]
| {gh-samples-url}javaconfig/rest[REST]
| Demonstrates how to use Spring Session in a REST application to support authenticating with a header.
| link:guides/java-rest.html[REST Guide]
| {gh-samples-url}javaconfig/users[Multiple Users]
| Demonstrates how to use Spring Session to manage multiple simultaneous browser sessions (i.e Google Accounts).
| link:guides/java-users.html[Multiple Users Guide]
|===
.Sample Applications using Spring XML based configuration
|===
| Source | Description | Guide
| {gh-samples-url}xml/redis[HttpSession with Redis]
| Demonstrates how to use Spring Session to replace the `HttpSession` with a Redis store.
| link:guides/xml-redis.html[HttpSession with Redis Guide]
| {gh-samples-url}xml/jdbc[HttpSession with JDBC]
| Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store.
| link:guides/xml-jdbc.html[HttpSession with JDBC Guide]
|===
.Misc sample Applications
|===
| Source | Description | Guide
| {gh-samples-url}misc/grails3[Grails 3]
| Demonstrates how to use Spring Session with Grails 3.
| link:guides/grails3.html[Grails 3 Guide]
| {gh-samples-url}misc/hazelcast[Hazelcast]
| Demonstrates how to use Spring Session with Hazelcast in a Java EE application.
| TBD
|===
@@ -81,11 +151,82 @@ We have already mentioned that Spring Session provides transparent integration w
=== HttpSession with Redis
Using Spring Session with `HttpSession` is enabled by adding a Servlet Filter before anything that uses the `HttpSession`.
You can choose from enabling this using either:
NOTE: The <<samples, HttpSession Sample>> provides a working sample on how to integrate Spring Session and `HttpSession`.
You can the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession Guide when integrating with your own application.
* <<httpsession-redis-jc,Java Based Configuration>>
* <<httpsession-redis-xml,XML Based Configuration>>
include::guides/httpsession.adoc[tags=config,leveloffset=+2]
[[httpsession-redis-jc]]
==== Redis Java Based Configuration
This section describes how to use Redis to back `HttpSession` using Java based configuration.
NOTE: The <<samples, HttpSession Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using Java configuration.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession Guide when integrating with your own application.
include::guides/java-redis.adoc[tags=config,leveloffset=+3]
[[httpsession-redis-xml]]
==== Redis XML Based Configuration
This section describes how to use Redis to back `HttpSession` using XML based configuration.
NOTE: The <<samples, HttpSession XML Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using XML configuration.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession XML Guide when integrating with your own application.
include::guides/xml-redis.adoc[tags=config,leveloffset=+3]
[[httpsession-jdbc]]
=== HttpSession with JDBC
Using Spring Session with `HttpSession` is enabled by adding a Servlet Filter before anything that uses the `HttpSession`.
You can choose from enabling this using either:
* <<httpsession-jdbc-jc,Java Based Configuration>>
* <<httpsession-jdbc-xml,XML Based Configuration>>
* <<httpsession-jdbc-boot,Spring Boot Based Configuration>>
[[httpsession-jdbc-jc]]
==== JDBC Java Based Configuration
This section describes how to use a relational database to back `HttpSession` using Java based configuration.
NOTE: The <<samples, HttpSession JDBC Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using Java configuration.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession JDBC Guide when integrating with your own application.
include::guides/java-jdbc.adoc[tags=config,leveloffset=+3]
[[httpsession-jdbc-xml]]
==== JDBC XML Based Configuration
This section describes how to use a relational database to back `HttpSession` using XML based configuration.
NOTE: The <<samples, HttpSession JDBC XML Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using XML configuration.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession JDBC XML Guide when integrating with your own application.
include::guides/xml-jdbc.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 <<samples, HttpSession JDBC Spring Boot Sample>> 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/boot-jdbc.adoc[tags=config,leveloffset=+3]
[[httpsession-hazelcast]]
=== HttpSession with Hazelcast
Using Spring Session with `HttpSession` is enabled by adding a Servlet Filter before anything that uses the `HttpSession`.
This section describes how to use Hazelcast to back `HttpSession` using Java based configuration.
NOTE: The <<samples, Hazelcast Spring Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using Java configuration.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed Hazelcast Spring Guide when integrating with your own application.
include::guides/java-hazelcast.adoc[tags=config,leveloffset=+2]
[[httpsession-how]]
=== How HttpSession Integration Works
@@ -102,19 +243,19 @@ It looks something like the following:
----
public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
public SessionRepositoryRequestWrapper(HttpServletRequest original) {
super(original);
}
public SessionRepositoryRequestWrapper(HttpServletRequest original) {
super(original);
}
public HttpSession getSession() {
return getSession(true);
}
public HttpSession getSession() {
return getSession(true);
}
public HttpSession getSession(boolean createNew) {
// create an HttpSession implementation from Spring Session
}
public HttpSession getSession(boolean createNew) {
// create an HttpSession implementation from Spring Session
}
// ... other methods delegate to the original HttpServletRequest ...
// ... other methods delegate to the original HttpServletRequest ...
}
----
@@ -128,15 +269,15 @@ The pseudocode can be found below:
----
public class SessionRepositoryFilter implements Filter {
public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
SessionRepositoryRequestWrapper customRequest =
new SessionRepositoryRequestWrapper(httpRequest);
public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
SessionRepositoryRequestWrapper customRequest =
new SessionRepositoryRequestWrapper(httpRequest);
chain.doFilter(customRequest, response, chain);
}
chain.doFilter(customRequest, response, chain);
}
// ...
// ...
}
----
@@ -150,28 +291,53 @@ Spring Session has the ability to support multiple sessions in a single browser
This provides the ability to support authenticating with multiple users in the same browser instance (i.e. Google Accounts).
NOTE: The <<samples,Manage Multiple Users Guide>> provides a complete working example of managing multiple users in the same browser instance.
You can the basic steps for integration below, but you are encouraged to follow along with the detailed Manage Multiple Users Guide when integrating with your own application.
You can follow the basic steps for integration below, but you are encouraged to follow along with the detailed Manage Multiple Users Guide when integrating with your own application.
include::guides/users.adoc[tags=how-does-it-work,leveloffset=+1]
include::guides/java-users.adoc[tags=how-does-it-work,leveloffset=+1]
[[httpsession-rest]]
=== HttpSession & RESTful APIs
Spring Session has can work with RESTful APIs by allowing the session to be provided in a header.
Spring Session can work with RESTful APIs by allowing the session to be provided in a header.
NOTE: The <<samples, REST Sample>> provides a working sample on how to use Spring Session in a REST application to support authenticating with a header.
You can the basic steps for integration below, but you are encouraged to follow along with the detailed REST Guide when integrating with your own application.
You can follow the basic steps for integration below, but you are encouraged to follow along with the detailed REST Guide when integrating with your own application.
include::guides/rest.adoc[tags=config,leveloffset=+1]
include::guides/java-rest.adoc[tags=config,leveloffset=+2]
[[httpsession-httpsessionlistener]]
=== HttpSessionListener
Spring Session supports `HttpSessionListener` by translating `SessionDestroyedEvent` and `SessionCreatedEvent` into `HttpSessionEvent` by declaring `SessionEventHttpSessionListenerAdapter`.
To use this support, you need to:
* Ensure your `SessionRepository` implementation supports and is configured to fire `SessionDestroyedEvent` and `SessionCreatedEvent`.
* Configure `SessionEventHttpSessionListenerAdapter` as a Spring bean.
* Inject every `HttpSessionListener` into the `SessionEventHttpSessionListenerAdapter`
If you are using the configuration support documented in <<httpsession-redis,HttpSession with Redis>>, then all you need to do is register every `HttpSessionListener` as a bean.
For example, assume you want to support Spring Security's concurrency control and need to use `HttpSessionEventPublisher` you can simply add `HttpSessionEventPublisher` as a bean.
In Java configuration, this might look like:
[source,java,indent=0]
----
include::{docs-test-dir}docs/http/RedisHttpSessionConfig.java[tags=config]
----
In XML configuration, this might look like:
[source,xml,indent=0]
----
include::{docs-test-resources-dir}docs/http/HttpSessionListenerXmlTests-context.xml[tags=config]
----
[[websocket]]
== WebSocket Integration
Spring Session provides transparent integration with Spring's WebSocket support.
include::guides/websocket.adoc[tags=disclaimer,leveloffset=+1]
include::guides/boot-websocket.adoc[tags=disclaimer,leveloffset=+1]
[[websocket-why]]
=== Why Spring Session & WebSockets?
@@ -190,14 +356,83 @@ This means that if we are actively chatting in our application and are not using
=== WebSocket Usage
The <<samples, WebSocket Sample>> provides a working sample on how to integrate Spring Session with WebSockets.
You can the basic steps for integration below, but you are encouraged to follow along with the detailed WebSocket Guide when integrating with your own application:
You can follow the basic steps for integration below, but you are encouraged to follow along with the detailed WebSocket Guide when integrating with your own application:
[[websocket-httpsession]]
==== HttpSession Integration
Before using WebSocket integration, you should be sure that you have <<httpsession>> working first.
include::guides/websocket.adoc[tags=config,leveloffset=+2]
include::guides/boot-websocket.adoc[tags=config,leveloffset=+2]
[[spring-security]]
== Spring Security Integration
Spring Session provides integration with Spring Security.
[[spring-security-rememberme]]
=== Spring Security Remember-Me Support
Spring Session provides integration with http://docs.spring.io/spring-security/site/docs/4.2.x/reference/htmlsingle/#remember-me[Spring Security's Remember-Me Authentication].
The support will:
* Change the session expiration length
* Ensure the session cookie expires at `Integer.MAX_VALUE`.
The cookie expiration is set to the largest possible value because the cookie is only set when the session is created.
If it were set to the same value as the session expiration, then the session would get renewed when the user used it but the cookie expiration would not be updated causing the expiration to be fixed.
To configure Spring Session with Spring Security in Java Configuration use the following as a guide:
[source,java,indent=0]
----
include::{docs-test-dir}docs/security/RememberMeSecurityConfiguration.java[tags=http-rememberme]
}
include::{docs-test-dir}docs/security/RememberMeSecurityConfiguration.java[tags=rememberme-bean]
----
An XML based configuration would look something like this:
[source,xml,indent=0]
----
include::{docs-test-resources-dir}docs/security/RememberMeSecurityConfigurationXmlTests-context.xml[tags=config]
----
[[spring-security-concurrent-sessions]]
=== Spring Security Concurrent Session Control
Spring Session provides integration with Spring Security to support its concurrent session control.
This allows limiting the number of active sessions that a single user can have concurrently, but unlike the default
Spring Security support this will also work in a clustered environment. This is done by providing a custom
implementation of Spring Security's `SessionRegistry` interface.
When using Spring Security's Java config DSL, you can configure the custom `SessionRegistry` through the
`SessionManagementConfigurer` like this:
[source,java,indent=0]
----
include::{docs-test-dir}docs/security/SecurityConfiguration.java[tags=class]
----
This assumes that you've also configured Spring Session to provide a `FindByIndexNameSessionRepository` that
returns `Session` instances.
When using XML configuration, it would look something like this:
[source,xml,indent=0]
----
include::{docs-test-resources-dir}docs/security/security-config.xml[tags=config]
----
This assumes that your Spring Session `SessionRegistry` bean is called `sessionRegistry`, which is the name used by all
`SpringHttpSessionConfiguration` subclasses.
[[spring-security-concurrent-sessions-limitations]]
=== Limitations
Spring Session's implementation of Spring Security's `SessionRegistry` interface does not support the `getAllPrincipals`
method, as this information cannot be retrieved using Spring Session. This method is never called by Spring Security,
so this only affects applications that access the `SessionRegistry` themselves.
[[api]]
== API Documentation
@@ -223,11 +458,7 @@ include::{indexdoc-tests}[tags=repository-demo]
<5> We retrieve the `Session` from the `SessionRepository`.
<6> We obtain the persisted `User` from our `Session` without the need for explicitly casting our attribute.
[[api-expiringsession]]
=== ExpiringSession
An `ExpiringSession` extends a `Session` by providing attributes related to the `Session` instance's expiration.
If there is no need to interact with the expiration information, prefer using the more simple `Session` API.
`Session` API also provides attributes related to the `Session` instance's expiration.
Typical usage might look like the following:
@@ -236,17 +467,17 @@ Typical usage might look like the following:
include::{indexdoc-tests}[tags=expire-repository-demo]
----
<1> We create a `SessionRepository` instance with a generic type, `S`, that extends `ExpiringSession`. The generic type is defined in our class.
<2> We create a new `ExpiringSession` using our `SessionRepository` and assign it to a variable of type `S`.
<3> We interact with the `ExpiringSession`.
In our example, we demonstrate updating the amount of time the `ExpiringSession` can be inactive before it expires.
<4> We now save the `ExpiringSession`.
<1> We create a `SessionRepository` instance with a generic type, `S`, that extends `Session`. The generic type is defined in our class.
<2> We create a new `Session` using our `SessionRepository` and assign it to a variable of type `S`.
<3> We interact with the `Session`.
In our example, we demonstrate updating the amount of time the `Session` can be inactive before it expires.
<4> We now save the `Session`.
This is why we needed the generic type `S`.
The `SessionRepository` only allows saving `ExpiringSession` instances that were created or retrieved using the same `SessionRepository`.
The `SessionRepository` only allows saving `Session` instances that were created or retrieved using the same `SessionRepository`.
This allows for the `SessionRepository` to make implementation specific optimizations (i.e. only writing attributes that have changed).
The last accessed time is automatically updated when the `ExpiringSession` is saved.
<5> We retrieve the `ExpiringSession` from the `SessionRepository`.
If the `ExpiringSession` were expired, the result would be null.
The last accessed time is automatically updated when the `Session` is saved.
<5> We retrieve the `Session` from the `SessionRepository`.
If the `Session` were expired, the result would be null.
[[api-sessionrepository]]
=== SessionRepository
@@ -256,12 +487,60 @@ A `SessionRepository` is in charge of creating, retrieving, and persisting `Sess
If possible, developers should not interact directly with a `SessionRepository` or a `Session`.
Instead, developers should prefer interacting with `SessionRepository` and `Session` indirectly through the <<httpsession,HttpSession>> and <<websocket,WebSocket>> integration.
[[api-findbyindexnamesessionrepository]]
=== FindByIndexNameSessionRepository
Spring Session's most basic API for using a `Session` is the `SessionRepository`.
This API is intentionally very simple, so that it is easy to provide additional implementations with basic functionality.
Some `SessionRepository` implementations may choose to implement `FindByIndexNameSessionRepository` also.
For example, Spring's Redis support implements `FindByIndexNameSessionRepository`.
The `FindByIndexNameSessionRepository` adds a single method to look up all the sessions for a particular user.
This is done by ensuring that the session attribute with the name `FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME` is populated with the username.
It is the responsibility of the developer to ensure the attribute is populated since Spring Session is not aware of the authentication mechanism being used.
An example of how this might be used can be seen below:
[source,java,indent=0]
----
include::{docs-test-dir}docs/FindByIndexNameSessionRepositoryTests.java[tags=set-username]
----
[NOTE]
====
Some implementations of `FindByIndexNameSessionRepository` will provide hooks to automatically index other session attributes.
For example, many implementations will automatically ensure the current Spring Security user name is indexed with the index name `FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME`.
====
Once the session is indexed, it can be found using the following:
[source,java,indent=0]
----
include::{docs-test-dir}docs/FindByIndexNameSessionRepositoryTests.java[tags=findby-username]
----
[[api-enablespringhttpsession]]
=== EnableSpringHttpSession
The `@EnableSpringHttpSession` annotation can be added to an `@Configuration` class to expose the `SessionRepositoryFilter` as a bean named "springSessionRepositoryFilter".
In order to leverage the annotation, a single `SessionRepository` bean must be provided.
For example:
[source,java,indent=0]
----
include::{docs-test-dir}docs/SpringHttpSessionConfig.java[tags=class]
----
It is important to note that no infrastructure for session expirations is configured for you out of the box.
This is because things like session expiration are highly implementation dependent.
This means if you require cleaning up expired sessions, you are responsible for cleaning up the expired sessions.
[[api-redisoperationssessionrepository]]
=== RedisOperationsSessionRepository
`RedisOperationsSessionRepository` is a `SessionRepository` that is implemented using Spring Data's `RedisOperations`.
In a web environment, this is typically used in combination with `SessionRepositoryFilter`.
The implementation supports `SessionDestroyedEvent` through `SessionMessageListener`.
The implementation supports `SessionDestroyedEvent` and `SessionCreatedEvent` through `SessionMessageListener`.
[[api-redisoperationssessionrepository-new]]
==== Instantiating a RedisOperationsSessionRepository
@@ -275,49 +554,72 @@ include::{indexdoc-tests}[tags=new-redisoperationssessionrepository]
For additional information on how to create a `RedisConnectionFactory`, refer to the Spring Data Redis Reference.
[[api-redisoperationssessionrepository-config]]
==== EnableRedisHttpSession
In a web environment, the simplest way to create a new `RedisOperationsSessionRepository` is to use `@EnableRedisHttpSession`.
Complete example usage can be found in the <<samples>>
You can use the following attributes to customize the configuration:
* **maxInactiveIntervalInSeconds** - the amount of time before the session will expire in seconds
* **redisNamespace** - allows configuring an application specific namespace for the sessions. Redis keys and channel ids will start with the prefix of `spring:session:<redisNamespace>:`.
* **redisFlushMode** - allows specifying when data will be written to Redis. The default is only when `save` is invoked on `SessionRepository`.
A value of `RedisFlushMode.IMMEDIATE` will write to Redis as soon as possible.
===== Custom RedisSerializer
You can customize the serialization by creating a Bean named `springSessionDefaultRedisSerializer` that implements `RedisSerializer<Object>`.
==== Redis TaskExecutor
`RedisOperationsSessionRepository` is subscribed to receive events from redis using a `RedisMessageListenerContainer`.
You can customize the way those events are dispatched, by creating a Bean named `springSessionRedisTaskExecutor` and/or a Bean `springSessionRedisSubscriptionExecutor`.
More details on configuring redis task executors can be found http://docs.spring.io/spring-data-redis/docs/current/reference/html/#redis:pubsub:subscribe:containers[here].
[[api-redisoperationssessionrepository-storage]]
==== Storage Details
The sections below outline how Redis is updated for each operation.
An example of creating a new session can be found below.
The subsequent sections describe the details.
----
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 \
maxInactiveInterval 1800 \
lastAccessedTime 1404360000000 \
sessionAttr:attrName someAttrValue \
sessionAttr2:attrName someAttrValue2
EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
EXPIRE spring:session:expirations1439245080000 2100
----
===== Saving a Session
Each session is stored in Redis as a Hash.
Each session is set and updated using the HMSET command.
An example of how each session is stored can be seen below.
HMSET spring:session:sessions:<session-id> creationTime 1404360000000 \
maxInactiveInterval 1800 lastAccessedTime 1404360000000 \
sessionAttr:<attrName> someAttrValue sessionAttr:<attrName2> someAttrValue2
[[api-redisoperationssessionrepository-expiration]]
===== Session Expiration
----
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 \
maxInactiveInterval 1800 \
lastAccessedTime 1404360000000 \
sessionAttr:attrName someAttrValue \
sessionAttr2:attrName someAttrValue2
----
An expiration is associated to each session using the EXPIRE command based upon the RedisOperationsSessionRepository.RedisSession.getMaxInactiveInterval().
For example:
In this example, the session following statements are true about the session:
EXPIRE spring:session:sessions:<session-id> 1800
Spring Session relies on the expired and delete http://redis.io/topics/notifications[keyspace notifications] from Redis to fire a <<SessionDestroyedEvent>>.
It is the `SessionDestroyedEvent` that ensures resources associated with the Session are cleaned up.
For example, when using Spring Session's WebSocket support the Redis expired or delete event is what triggers any WebSocket connections associated with the session to be closed.
One problem with this approach is that Redis makes no guarantee of when the expired event will be fired if they key has not been accessed.
Specifically the background task that Redis uses to clean up expired keys is a low priority task and may not trigger the key expiration.
For additional details see http://redis.io/topics/notifications[Timing of expired events] section in the Redis documentation.
To circumvent the fact that expired events are not guaranteed to happen we can ensure that each key is accessed when it is expected to expire.
This means that if the TTL is expired on the key, Redis will remove the key and fire the expired event when we try to access they key.
For this reason, each session expiration is also tracked to the nearest minute.
This allows a background task to access the potentially expired sessions to ensure that Redis expired events are fired in a more deterministic fashion.
For example:
SADD spring:session:expirations:<expire-rounded-up-to-nearest-minute> <session-id>
EXPIRE spring:session:expirations:<expire-rounded-up-to-nearest-minute> 1800
The background task will then use these mappings to explicitly request each key.
By accessing they key, rather than deleting it, we ensure that Redis deletes the key for us only if the TTL is expired.
NOTE: We do not explicitly delete the keys since in some instances there may be a race condition that incorrectly identifies a key as expired when it is not.
Short of using distributed locks (which would kill our performance) there is no way to ensure the consistency of the expiration mapping.
By simply accessing the key, we ensure that the key is only removed if the TTL on that key is expired.
* The session id is 33fdd1b6-b496-4b33-9f7d-df96679d32fe
* The session was created at 1404360000000 in milliseconds since midnight of 1/1/1970 GMT.
* The session expires in 1800 seconds (30 minutes).
* The session was last accessed at 1404360000000 in milliseconds since midnight of 1/1/1970 GMT.
* The session has two attributes.
The first is "attrName" with the value of "someAttrValue".
The second session attribute is named "attrName2" with the value of "someAttrValue2".
[[api-redisoperationssessionrepository-writes]]
===== Optimized Writes
@@ -327,27 +629,116 @@ This means if an attribute is written once and read many times we only need to w
For example, assume the session attribute "sessionAttr2" from earlier was updated.
The following would be executed upon saving:
HMSET spring:session:sessions:<session-id> sessionAttr:<attrName2> newValue
EXPIRE spring:session:sessions:<session-id> 1800
----
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe sessionAttr:attrName2 newValue
----
[[api-redisoperationssessionrepository-sessiondestroyedevent]]
==== SessionDestroyedEvent
[[api-redisoperationssessionrepository-expiration]]
===== Session Expiration
`RedisOperationsSessionRepository` supports firing a `SessionDestroyedEvent` whenever a `Session` is deleted or when it expires.
This is necessary to ensure resources associated with the `Session` are properly cleaned up.
For example, when integrating with WebSockets the `SessionDestroyedEvent` is in charge of closing any active WebSocket connections.
Firing a `SessionDestroyedEvent` is made available through the `SessionMessageListener` which listens to http://redis.io/topics/notifications[Redis Keyspace events].
In order for this to work, Redis Keyspace events for Generic commands and Expired events needs to be enabled.
An expiration is associated to each session using the EXPIRE command based upon the `Session.getMaxInactiveInterval()`.
For example:
TIP: If you are using `@EnableRedisHttpSession` the `SessionMessageListener` and enabling the necessary Redis Keyspace events is done automatically.
----
EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
----
You will note that the expiration that is set is 5 minutes after the session actually expires.
This is necessary so that the value of the session can be accessed when the session expires.
An expiration is set on the session itself five minutes after it actually expires to ensure it is cleaned up, but only after we perform any necessary processing.
[NOTE]
====
The `SessionRepository.findById(String)` method ensures that no expired sessions will be returned.
This means there is no need to check the expiration before using a session.
====
Spring Session relies on the delete and expired http://redis.io/topics/notifications[keyspace notifications] from Redis to fire a <<api-redisoperationssessionrepository-sessiondestroyedevent,SessionDeletedEvent>> and <<api-redisoperationssessionrepository-sessiondestroyedevent,SessionExpiredEvent>> respectively.
It is the `SessionDeletedEvent` or `SessionExpiredEvent` that ensures resources associated with the Session are cleaned up.
For example, when using Spring Session's WebSocket support the Redis expired or delete event is what triggers any WebSocket connections associated with the session to be closed.
Expiration is not tracked directly on the session key itself since this would mean the session data would no longer be available. Instead a special session expires key is used. In our example the expires key is:
----
APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
----
When a session expires key is deleted or expires, the keyspace notification triggers a lookup of the actual session and a SessionDestroyedEvent is fired.
One problem with relying on Redis expiration exclusively is that Redis makes no guarantee of when the expired event will be fired if the key has not been accessed.
Specifically the background task that Redis uses to clean up expired keys is a low priority task and may not trigger the key expiration.
For additional details see http://redis.io/topics/notifications[Timing of expired events] section in the Redis documentation.
To circumvent the fact that expired events are not guaranteed to happen we can ensure that each key is accessed when it is expected to expire.
This means that if the TTL is expired on the key, Redis will remove the key and fire the expired event when we try to access the key.
For this reason, each session expiration is also tracked to the nearest minute.
This allows a background task to access the potentially expired sessions to ensure that Redis expired events are fired in a more deterministic fashion.
For example:
----
SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
EXPIRE spring:session:expirations1439245080000 2100
----
The background task will then use these mappings to explicitly request each key.
By accessing the key, rather than deleting it, we ensure that Redis deletes the key for us only if the TTL is expired.
[NOTE]
====
We do not explicitly delete the keys since in some instances there may be a race condition that incorrectly identifies a key as expired when it is not.
Short of using distributed locks (which would kill our performance) there is no way to ensure the consistency of the expiration mapping.
By simply accessing the key, we ensure that the key is only removed if the TTL on that key is expired.
====
[[api-redisoperationssessionrepository-sessiondestroyedevent]]
==== SessionDeletedEvent and SessionExpiredEvent
`SessionDeletedEvent` and `SessionExpiredEvent` are both types of `SessionDestroyedEvent`.
`RedisOperationsSessionRepository` supports firing a `SessionDeletedEvent` whenever a `Session` is deleted or a `SessionExpiredEvent` when it expires.
This is necessary to ensure resources associated with the `Session` are properly cleaned up.
For example, when integrating with WebSockets the `SessionDestroyedEvent` is in charge of closing any active WebSocket connections.
Firing `SessionDeletedEvent` or `SessionExpiredEvent` is made available through the `SessionMessageListener` which listens to http://redis.io/topics/notifications[Redis Keyspace events].
In order for this to work, Redis Keyspace events for Generic commands and Expired events needs to be enabled.
For example:
[source,bash]
----
redis-cli config set notify-keyspace-events Egx
----
If you are using `@EnableRedisHttpSession` the `SessionMessageListener` and enabling the necessary Redis Keyspace events is done automatically.
However, in a secured Redis enviornment the config command is disabled.
This means that Spring Session cannot configure Redis Keyspace events for you.
To disable the automatic configuration add `ConfigureRedisAction.NO_OP` as a bean.
For example, Java Configuration can use the following:
[source,java,indent=0]
----
include::{docs-test-dir}docs/RedisHttpSessionConfigurationNoOpConfigureRedisActionTests.java[tags=configure-redis-action]
----
XML Configuration can use the following:
[source,xml,indent=0]
----
include::{docs-test-resources-dir}docs/HttpSessionConfigurationNoOpConfigureRedisActionXmlTests-context.xml[tags=configure-redis-action]
----
[[api-redisoperationssessionrepository-sessioncreatedevent]]
==== SessionCreatedEvent
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 the session that was created.
If registered as a MessageListener (default), then `RedisOperationsSessionRepository` will then translate the Redis message into a `SessionCreatedEvent`.
[[api-redisoperationssessionrepository-cli]]
==== Viewing the Session in Redis
@@ -381,7 +772,7 @@ redis 127.0.0.1:6379> hget spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed
[[api-mapsessionrepository]]
=== MapSessionRepository
The `MapSessionRepository` allows for persisting `ExpiringSession` in a `Map` with the key being the `ExpiringSession` id and the value being the `ExpiringSession`.
The `MapSessionRepository` allows for persisting `Session` in a `Map` with the key being the `Session` id and the value being the `Session`.
The implementation can be used with a `ConcurrentHashMap` as a testing or convenience mechanism.
Alternatively, it can be used with distributed `Map` implementations. For example, it can be used with Hazelcast.
@@ -399,9 +790,139 @@ include::{indexdoc-tests}[tags=new-mapsessionrepository]
==== Using Spring Session and Hazlecast
The <<samples,Hazelcast Sample>> is a complete application demonstrating using Spring Session with Hazelcast.
To run it use the following:
./gradlew :samples:hazelcast:tomcatRun
./gradlew :samples:hazelcast:tomcatRun
The <<samples,Hazelcast Spring Sample>> is a complete application demonstrating using Spring Session with Hazelcast and Spring Security.
It includes example Hazelcast `MapListener` implementations that support firing `SessionCreatedEvent`, `SessionDeletedEvent` and `SessionExpiredEvent`.
To run it use the following:
./gradlew :samples:hazelcast-spring:tomcatRun
[[api-jdbcoperationssessionrepository]]
=== JdbcOperationsSessionRepository
`JdbcOperationsSessionRepository` is a `SessionRepository` implementation that uses Spring's `JdbcOperations` to store sessions in a relational database.
In a web environment, this is typically used in combination with `SessionRepositoryFilter`.
Please note that this implementation does not support publishing of session events.
[[api-jdbcoperationssessionrepository-new]]
==== Instantiating a JdbcOperationsSessionRepository
A typical example of how to create a new instance can be seen below:
[source,java,indent=0]
----
include::{indexdoc-tests}[tags=new-jdbcoperationssessionrepository]
----
For additional information on how to create and configure `JdbcTemplate` and `PlatformTransactionManager`, refer to the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html[Spring Framework Reference Documentation].
[[api-jdbcoperationssessionrepository-config]]
==== EnableJdbcHttpSession
In a web environment, the simplest way to create a new `JdbcOperationsSessionRepository` is to use `@EnableJdbcHttpSession`.
Complete example usage can be found in the <<samples>>
You can use the following attributes to customize the configuration:
* **tableName** - the name of database table used by Spring Session to store sessions
* **maxInactiveIntervalInSeconds** - the amount of time before the session will expire in seconds
===== Custom LobHandler
You can customize the BLOB handling by creating a Bean named `springSessionLobHandler` that implements `LobHandler`.
===== Custom ConversionService
You can customize the default serialization and deserialization of the session by providing a `ConversionService` instance.
When working in a typical Spring environment, the default `ConversionService` Bean (named `conversionService`) will be automatically picked up and used for serialization and deserialization.
However, you can override the default `ConversionService` by providing a Bean named `springSessionConversionService`.
[[api-jdbcoperationssessionrepository-storage]]
==== Storage Details
By default, this implementation uses `SPRING_SESSION` and `SPRING_SESSION_ATTRIBUTES` tables to store sessions.
Note that the table name can be easily customized as already described. In that case the table used to store attributes will be named using the provided table name, suffixed with `_ATTRIBUTES`.
If further customizations are needed, SQL queries used by the repository can be customized using `set*Query` setter methods. In this case you need to manually configure the `sessionRepository` bean.
Due to the differences between the various database vendors, especially when it comes to storing binary data, make sure to use SQL script specific to your database.
Scripts for most major database vendors are packaged as `org/springframework/session/jdbc/schema-\*.sql`, where `*` is the target database type.
For example, with PostgreSQL database you would use the following schema script:
[source,sql,indent=0]
----
include::{session-jdbc-main-resources-dir}org/springframework/session/jdbc/schema-postgresql.sql[]
----
And with MySQL database:
[source,sql,indent=0]
----
include::{session-jdbc-main-resources-dir}org/springframework/session/jdbc/schema-mysql.sql[]
----
==== Transaction management
All JDBC operations in `JdbcOperationsSessionRepository` are executed in a transactional manner.
Transactions are executed with propagation set to `REQUIRES_NEW` in order to avoid unexpected behavior due to interference with existing transactions (for example, executing `save` operation in a thread that already participates in a read-only transaction).
[[api-hazelcastsessionrepository]]
=== HazelcastSessionRepository
`HazelcastSessionRepository` is a `SessionRepository` implementation that stores sessions in Hazelcast's distributed `IMap`.
In a web environment, this is typically used in combination with `SessionRepositoryFilter`.
[[api-hazelcastsessionrepository-new]]
==== Instantiating a HazelcastSessionRepository
A typical example of how to create a new instance can be seen below:
[source,java,indent=0]
----
include::{indexdoc-tests}[tags=new-hazelcastsessionrepository]
----
For additional information on how to create and configure Hazelcast instance, refer to the http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[Hazelcast documentation].
[[api-enablehazelcasthttpsession]]
==== EnableHazelcastHttpSession
If you wish to use http://hazelcast.org/[Hazelcast] as your backing source for the `SessionRepository`, then the `@EnableHazelcastHttpSession` annotation
can be added to an `@Configuration` class. This extends the functionality provided by the `@EnableSpringHttpSession` annotation but makes the `SessionRepository` for you in Hazelcast.
You must provide a single `HazelcastInstance` bean for the configuration to work.
Complete configuration example can be found in the <<samples>>
[[api-enablehazelcasthttpsession-customize]]
==== Basic Customization
You can use the following attributes on `@EnableHazelcastHttpSession` to customize the configuration:
* **maxInactiveIntervalInSeconds** - the amount of time before the session will expire in seconds. Default is 1800 seconds (30 minutes)
* **sessionMapName** - the name of the distributed `Map` that will be used in Hazelcast to store the session data.
[[api-enablehazelcasthttpsession-events]]
==== Session Events
Using a `MapListener` to respond to entries being added, evicted, and removed from the distributed `Map`, these events will trigger
publishing `SessionCreatedEvent`, `SessionExpiredEvent`, and `SessionDeletedEvent` events respectively using the `ApplicationEventPublisher`.
[[api-enablehazelcasthttpsession-storage]]
==== Storage Details
Sessions will be stored in a distributed `IMap` in Hazelcast.
The `IMap` interface methods will be used to `get()` and `put()` Sessions.
Additionally, `values()` method is used to support `FindByIndexNameSessionRepository#findByIndexNameAndIndexValue` operation, together with appropriate `ValueExtractor` that needs to be registered with Hazelcast. Refer to <<samples, Hazelcast Spring Sample>> for more details on this configuration.
The expiration of a session in the `IMap` is handled by Hazelcast's support for setting the time to live on an entry when it is `put()` into the `IMap`. Entries (sessions) that have been idle longer than the time to live will be automatically removed from the `IMap`.
You shouldn't need to configure any settings such as `max-idle-seconds` or `time-to-live-seconds` for the `IMap` within the Hazelcast configuration.
Note that if you use Hazelcast's `MapStore` to persist your sessions `IMap` there are some limitations when reloading the sessions from `MapStore`:
* reload triggers `EntryAddedListener` which results in `SessionCreatedEvent` being re-published
* reload uses default TTL for a given `IMap` which results in sessions losing their original TTL
[[community]]
== Spring Session Community
@@ -434,3 +955,34 @@ We appreciate https://help.github.com/articles/using-pull-requests/[Pull Request
=== License
Spring Session is Open Source software released under the http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
[[community-extensions]]
=== Community Extensions
|===
| Name | Location
| Spring Session OrientDB
| https://github.com/maseev/spring-session-orientdb
| Spring Session Infinispan
| http://infinispan.org/docs/dev/user_guide/user_guide.html#externalizing_session_using_spring_session
|===
[[minimum-requirements]]
== Minimum Requirements
The minimum requirements for Spring Session are:
* Java 8+
* If you are running in a Servlet Container (not required), Servlet 3.1+
* If you are using other Spring libraries (not required), the minimum required version is Spring 5.0.x.
* `@EnableRedisHttpSession` requires Redis 2.8+. This is necessary to support <<api-redisoperationssessionrepository-expiration,Session Expiration>>
* `@EnableHazelcastHttpSession` requires Hazelcast 3.6+. This is necessary to support <<api-enablehazelcasthttpsession-storage,`FindByIndexNameSessionRepository`>>
[NOTE]
====
At its core Spring Session only has a required dependency on `spring-jcl`.
For an example of using Spring Session without any other Spring dependencies, refer to the <<samples,hazelcast sample>> application.
====

View File

@@ -0,0 +1,20 @@
/*
* 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 docs;
public class Docs {
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2014-2017 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 docs;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
/**
* @author Rob Winch
*
*/
@RunWith(MockitoJUnitRunner.class)
public class FindByIndexNameSessionRepositoryTests {
@Mock
FindByIndexNameSessionRepository<Session> sessionRepository;
@Mock
Session session;
@Test
public void setUsername() {
// tag::set-username[]
String username = "username";
this.session.setAttribute(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
// end::set-username[]
}
@Test
@SuppressWarnings("unused")
public void findByUsername() {
// tag::findby-username[]
String username = "username";
Map<String, Session> sessionIdToSession = this.sessionRepository
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
username);
// end::findby-username[]
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2014-2017 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 docs;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.session.Session;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* @author Rob Winch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class HttpSessionConfigurationNoOpConfigureRedisActionXmlTests {
@Autowired
SessionRepositoryFilter<? extends Session> filter;
@Test
public void redisConnectionFactoryNotUsedSinceNoValidation() {
assertThat(this.filter).isNotNull();
}
static RedisConnectionFactory connectionFactory() {
return mock(RedisConnectionFactory.class);
}
}

View File

@@ -1,110 +1,185 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2014-2017 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
* 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
* 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.
* 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 docs;
import org.junit.Test;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.session.*;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import java.time.Duration;
import java.util.concurrent.ConcurrentHashMap;
import static org.fest.assertions.Assertions.assertThat;
import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import org.junit.Test;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.mock.web.MockServletContext;
import org.springframework.session.MapSession;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.hazelcast.HazelcastSessionRepository;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
* @author Vedran Pavic
*/
public class IndexDocTests {
static final String ATTR_USER = "user";
static final String ATTR_USER = "user";
@Test
public void repositoryDemo() {
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
demo.repository = new MapSessionRepository();
@Test
public void repositoryDemo() {
RepositoryDemo<MapSession> demo = new RepositoryDemo<>();
demo.repository = new MapSessionRepository(new ConcurrentHashMap<>());
demo.demo();
}
demo.demo();
}
// tag::repository-demo[]
public class RepositoryDemo<S extends Session> {
private SessionRepository<S> repository; // <1>
// tag::repository-demo[]
public class RepositoryDemo<S extends Session> {
private SessionRepository<S> repository; // <1>
public void demo() {
S toSave = repository.createSession(); // <2>
public void demo() {
S toSave = this.repository.createSession(); // <2>
// <3>
User rwinch = new User("rwinch");
toSave.setAttribute(ATTR_USER, rwinch);
// <3>
User rwinch = new User("rwinch");
toSave.setAttribute(ATTR_USER, rwinch);
repository.save(toSave); // <4>
this.repository.save(toSave); // <4>
S session = repository.getSession(toSave.getId()); // <5>
S session = this.repository.findById(toSave.getId()); // <5>
// <6>
User user = session.getAttribute(ATTR_USER);
assertThat(user).isEqualTo(rwinch);
}
// <6>
User user = session.getAttribute(ATTR_USER);
assertThat(user).isEqualTo(rwinch);
}
// ... setter methods ...
}
// end::repository-demo[]
// ... setter methods ...
}
// end::repository-demo[]
@Test
public void expireRepositoryDemo() {
ExpiringRepositoryDemo<MapSession> demo = new ExpiringRepositoryDemo<>();
demo.repository = new MapSessionRepository(new ConcurrentHashMap<>());
@Test
public void expireRepositoryDemo() {
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
demo.repository = new MapSessionRepository();
demo.demo();
}
demo.demo();
}
// tag::expire-repository-demo[]
public class ExpiringRepositoryDemo<S extends Session> {
private SessionRepository<S> repository; // <1>
// tag::expire-repository-demo[]
public class ExpiringRepositoryDemo<S extends ExpiringSession> {
private SessionRepository<S> repository; // <1>
public void demo() {
S toSave = this.repository.createSession(); // <2>
// ...
toSave.setMaxInactiveInterval(Duration.ofSeconds(30)); // <3>
public void demo() {
S toSave = repository.createSession(); // <2>
// ...
toSave.setMaxInactiveIntervalInSeconds(30); // <3>
this.repository.save(toSave); // <4>
repository.save(toSave); // <4>
S session = this.repository.findById(toSave.getId()); // <5>
// ...
}
S session = repository.getSession(toSave.getId()); // <5>
// ...
}
// ... setter methods ...
}
// end::expire-repository-demo[]
// ... setter methods ...
}
// end::expire-repository-demo[]
@Test
@SuppressWarnings("unused")
public void newRedisOperationsSessionRepository() {
// tag::new-redisoperationssessionrepository[]
LettuceConnectionFactory factory = new LettuceConnectionFactory();
SessionRepository<? extends Session> repository = new RedisOperationsSessionRepository(
factory);
// end::new-redisoperationssessionrepository[]
}
@Test
public void newRedisOperationsSessionRepository() {
// tag::new-redisoperationssessionrepository[]
JedisConnectionFactory factory = new JedisConnectionFactory();
SessionRepository<? extends ExpiringSession> repository =
new RedisOperationsSessionRepository(factory);
// end::new-redisoperationssessionrepository[]
}
@Test
@SuppressWarnings("unused")
public void mapRepository() {
// tag::new-mapsessionrepository[]
SessionRepository<? extends Session> repository = new MapSessionRepository(
new ConcurrentHashMap<>());
// end::new-mapsessionrepository[]
}
@Test
public void mapRepository() {
// tag::new-mapsessionrepository[]
SessionRepository<? extends ExpiringSession> repository = new MapSessionRepository();
// end::new-mapsessionrepository[]
}
@Test
@SuppressWarnings("unused")
public void newJdbcOperationsSessionRepository() {
// tag::new-jdbcoperationssessionrepository[]
JdbcTemplate jdbcTemplate = new JdbcTemplate();
private static class User {
private User(String username) {}
}
// ... configure JdbcTemplate ...
PlatformTransactionManager transactionManager = new DataSourceTransactionManager();
// ... configure transactionManager ...
SessionRepository<? extends Session> repository =
new JdbcOperationsSessionRepository(jdbcTemplate, transactionManager);
// end::new-jdbcoperationssessionrepository[]
}
@Test
@SuppressWarnings("unused")
public void newHazelcastSessionRepository() {
// tag::new-hazelcastsessionrepository[]
Config config = new Config();
// ... configure Hazelcast ...
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);
IMap<String, MapSession> sessions = hazelcastInstance
.getMap("spring:session:sessions");
HazelcastSessionRepository repository =
new HazelcastSessionRepository(sessions);
// end::new-hazelcastsessionrepository[]
}
@Test
public void runSpringHttpSessionConfig() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringHttpSessionConfig.class);
context.setServletContext(new MockServletContext());
context.refresh();
try {
context.getBean(SessionRepositoryFilter.class);
}
finally {
context.close();
}
}
private static final class User {
private User(String username) {
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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 docs;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.mockito.Mockito.mock;
/**
* @author Rob Winch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class RedisHttpSessionConfigurationNoOpConfigureRedisActionTests {
@Test
public void redisConnectionFactoryNotUsedSinceNoValidation() {
}
@EnableRedisHttpSession
@Configuration
static class Config {
// tag::configure-redis-action[]
@Bean
public static ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
// end::configure-redis-action[]
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return mock(RedisConnectionFactory.class);
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2014-2017 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 docs;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
// tag::class[]
@EnableSpringHttpSession
@Configuration
public class SpringHttpSessionConfig {
@Bean
public MapSessionRepository sessionRepository() {
return new MapSessionRepository(new ConcurrentHashMap<>());
}
}
// end::class[]

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2014-2017 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 docs.http;
import java.util.Properties;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.core.session.SessionDestroyedEvent;
import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* @author Rob Winch
* @author Mark Paluch
* @since 1.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public abstract class AbstractHttpSessionListenerTests {
@Autowired
ApplicationEventPublisher publisher;
@Autowired
SecuritySessionDestroyedListener listener;
@Test
public void springSessionDestroyedTranslatedToSpringSecurityDestroyed() {
Session session = new MapSession();
this.publisher.publishEvent(
new org.springframework.session.events.SessionDestroyedEvent(this,
session));
assertThat(this.listener.getEvent().getId()).isEqualTo(session.getId());
}
static RedisConnectionFactory createMockRedisConnection() {
RedisConnectionFactory factory = mock(RedisConnectionFactory.class);
RedisConnection connection = mock(RedisConnection.class);
given(factory.getConnection()).willReturn(connection);
given(connection.getConfig(anyString())).willReturn(new Properties());
return factory;
}
static class SecuritySessionDestroyedListener
implements ApplicationListener<SessionDestroyedEvent> {
private SessionDestroyedEvent event;
/*
* (non-Javadoc)
*
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.
* springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(SessionDestroyedEvent event) {
this.event = event;
}
public SessionDestroyedEvent getEvent() {
return this.event;
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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 docs.http;
import com.hazelcast.config.Config;
import com.hazelcast.config.MapAttributeConfig;
import com.hazelcast.config.MapIndexConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.hazelcast.HazelcastSessionRepository;
import org.springframework.session.hazelcast.PrincipalNameExtractor;
import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
//tag::config[]
@EnableHazelcastHttpSession // <1>
@Configuration
public class HazelcastHttpSessionConfig {
@Bean
public HazelcastInstance hazelcastInstance() {
MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
Config config = new Config();
config.getMapConfig("spring:session:sessions") // <2>
.addMapAttributeConfig(attributeConfig)
.addMapIndexConfig(new MapIndexConfig(
HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
return Hazelcast.newHazelcastInstance(config); // <3>
}
}
// end::config[]

View File

@@ -0,0 +1,45 @@
/*
* 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 docs.http;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Rob Winch
*
*/
@ContextConfiguration(classes = { HttpSessionListenerJavaConfigTests.MockConfig.class,
RedisHttpSessionConfig.class })
public class HttpSessionListenerJavaConfigTests extends AbstractHttpSessionListenerTests {
@Configuration
static class MockConfig {
@Bean
public static RedisConnectionFactory redisConnectionFactory() {
return AbstractHttpSessionListenerTests.createMockRedisConnection();
}
@Bean
public SecuritySessionDestroyedListener securitySessionDestroyedListener() {
return new SecuritySessionDestroyedListener();
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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 docs.http;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Rob Winch
*
*/
@ContextConfiguration
public class HttpSessionListenerXmlTests extends AbstractHttpSessionListenerTests {
}

View File

@@ -0,0 +1,36 @@
/*
* 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 docs.http;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
// tag::config[]
@Configuration
@EnableRedisHttpSession
public class RedisHttpSessionConfig {
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
// ...
}
// end::config[]

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2014-2017 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 docs.security;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.context.annotation.Bean;
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;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
/**
* @author rwinch
*/
@EnableWebSecurity
@EnableSpringHttpSession
public class RememberMeSecurityConfiguration extends WebSecurityConfigurerAdapter {
// @formatter:off
// tag::http-rememberme[]
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ... additional configuration ...
.rememberMe()
.rememberMeServices(rememberMeServices());
// end::http-rememberme[]
http
.formLogin().and()
.authorizeRequests()
.anyRequest().authenticated();
}
// tag::rememberme-bean[]
@Bean
RememberMeServices rememberMeServices() {
SpringSessionRememberMeServices rememberMeServices =
new SpringSessionRememberMeServices();
// optionally customize
rememberMeServices.setAlwaysRemember(true);
return rememberMeServices;
}
// end::rememberme-bean[]
// @formatter:on
@Override
@Bean
public InMemoryUserDetailsManager userDetailsService() {
InMemoryUserDetailsManager uds = new InMemoryUserDetailsManager();
uds.createUser(
User.withUsername("user").password("password").roles("USER").build());
return uds;
}
@Bean
MapSessionRepository sessionRepository() {
return new MapSessionRepository(new ConcurrentHashMap<>());
}
}
// end::class[]

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2014-2017 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 docs.security;
import java.time.Duration;
import java.util.Base64;
import javax.servlet.http.Cookie;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.test.context.ContextConfiguration;
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.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
/**
* @author rwinch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RememberMeSecurityConfiguration.class)
@WebAppConfiguration
@SuppressWarnings("rawtypes")
public class RememberMeSecurityConfigurationTests<T extends Session> {
@Autowired
WebApplicationContext context;
@Autowired
SessionRepositoryFilter springSessionRepositoryFilter;
@Autowired
SessionRepository<T> sessions;
MockMvc mockMvc;
@Before
public void setup() {
// @formatter:off
this.mockMvc = MockMvcBuilders
.webAppContextSetup(this.context)
.addFilters(this.springSessionRepositoryFilter)
.apply(springSecurity())
.build();
// @formatter:on
}
@Test
public void authenticateWhenSpringSessionRememberMeEnabledThenCookieMaxAgeAndSessionExpirationSet()
throws Exception {
// @formatter:off
MvcResult result = this.mockMvc
.perform(formLogin())
.andReturn();
// @formatter:on
Cookie cookie = result.getResponse().getCookie("SESSION");
assertThat(cookie.getMaxAge()).isEqualTo(Integer.MAX_VALUE);
T session = this.sessions
.findById(new String(Base64.getDecoder().decode(cookie.getValue())));
assertThat(session.getMaxInactiveInterval())
.isEqualTo(Duration.ofDays(30));
}
}
// end::class[]

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2014-2017 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 docs.security;
import java.time.Duration;
import java.util.Base64;
import javax.servlet.http.Cookie;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.test.context.ContextConfiguration;
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.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
/**
* @author rwinch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
@SuppressWarnings("rawtypes")
public class RememberMeSecurityConfigurationXmlTests<T extends Session> {
@Autowired
WebApplicationContext context;
@Autowired
SessionRepositoryFilter springSessionRepositoryFilter;
@Autowired
SessionRepository<T> sessions;
MockMvc mockMvc;
@Before
public void setup() {
// @formatter:off
this.mockMvc = MockMvcBuilders
.webAppContextSetup(this.context)
.addFilters(this.springSessionRepositoryFilter)
.apply(springSecurity())
.build();
// @formatter:on
}
@Test
public void authenticateWhenSpringSessionRememberMeEnabledThenCookieMaxAgeAndSessionExpirationSet()
throws Exception {
// @formatter:off
MvcResult result = this.mockMvc
.perform(formLogin())
.andReturn();
// @formatter:on
Cookie cookie = result.getResponse().getCookie("SESSION");
assertThat(cookie.getMaxAge()).isEqualTo(Integer.MAX_VALUE);
T session = this.sessions
.findById(new String(Base64.getDecoder().decode(cookie.getValue())));
assertThat(session.getMaxInactiveInterval())
.isEqualTo(Duration.ofDays(30));
}
}
// end::class[]

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2014-2017 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 docs.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
/**
* @author Joris Kuipers
*/
// tag::class[]
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private FindByIndexNameSessionRepository<Session> sessionRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
// other config goes here...
.sessionManagement()
.maximumSessions(2)
.sessionRegistry(sessionRegistry());
// @formatter:on
}
@Bean
SpringSessionBackedSessionRegistry sessionRegistry() {
return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);
}
}
// end::class[]

View File

@@ -1,18 +1,19 @@
/*
* Copyright 2002-2014 the original author or authors.
* 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
* 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
* 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.
* 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 docs.websocket;
import org.springframework.context.annotation.Configuration;
@@ -23,24 +24,22 @@ import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBr
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
/**
* @author Rob Winch
*/
* @author Rob Winch
*/
// tag::class[]
@Configuration
@EnableScheduling
@EnableWebSocketMessageBroker
public class WebSocketConfig
extends AbstractWebSocketMessageBrokerConfigurer {
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/messages")
.withSockJS();
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/messages").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
}
// end::class[]
// end::class[]

View File

@@ -1,30 +0,0 @@
/*
* Copyright 2002-2014 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 docs.websocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
/**
* @author Rob Winch
*/
public class WebSocketDocTests {
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
<!-- tag::configure-redis-action[] -->
<util:constant
static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
<!-- end::configure-redis-action[] -->
<bean class="docs.HttpSessionConfigurationNoOpConfigureRedisActionXmlTests"
factory-method="connectionFactory"/>
</beans>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">
<!-- tag::config[] -->
<bean class="org.springframework.security.web.session.HttpSessionEventPublisher"/>
<!-- end::config[] -->
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
<bean class="docs.http.AbstractHttpSessionListenerTests"
factory-method="createMockRedisConnection"/>
<bean class="docs.http.AbstractHttpSessionListenerTests$SecuritySessionDestroyedListener"/>
</beans>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- tag::config[] -->
<security:http>
<!-- ... -->
<security:form-login />
<security:remember-me services-ref="rememberMeServices"/>
</security:http>
<bean id="rememberMeServices"
class="org.springframework.session.security.web.authentication.SpringSessionRememberMeServices"
p:alwaysRemember="true"/>
<!-- end::config[] -->
<security:user-service>
<security:user name="user" password="password" authorities="ROLE_USER"/>
</security:user-service>
<bean class="org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration"/>
<bean id="springSessionRepository" class="org.springframework.session.MapSessionRepository">
<constructor-arg>
<bean class="java.util.concurrent.ConcurrentHashMap"/>
</constructor-arg>
</bean>
</beans>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<!-- tag::config[] -->
<security:http>
<!-- other config goes here... -->
<security:session-management>
<security:concurrency-control max-sessions="2" session-registry-ref="sessionRegistry"/>
</security:session-management>
</security:http>
<bean id="sessionRegistry"
class="org.springframework.session.security.SpringSessionBackedSessionRegistry">
<constructor-arg ref="sessionRepository"/>
</bean>
<!-- end::config[] -->
</beans>

11
eclipse/.checkstyle Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
<local-check-config name="Spring Session Checkstyle" location="${configDir}/checkstyle.xml" type="external" description="">
<property name="configDir" value="${configDir}"/>
<additional-data name="protect-config-file" value="false"/>
</local-check-config>
<fileset name="all" enabled="true" check-config-name="Spring Session Checkstyle" local="true">
<file-match-pattern match-pattern="." include-pattern="true"/>
</fileset>
</fileset-config>

View File

@@ -0,0 +1,295 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="12">
<profile kind="CodeFormatterProfile" name="Spring Session Java Conventions" version="12">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="90"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="90"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>
</profiles>

View File

@@ -0,0 +1,391 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.codeComplete.argumentPrefixes=
org.eclipse.jdt.core.codeComplete.argumentSuffixes=
org.eclipse.jdt.core.codeComplete.fieldPrefixes=
org.eclipse.jdt.core.codeComplete.fieldSuffixes=
org.eclipse.jdt.core.codeComplete.localPrefixes=
org.eclipse.jdt.core.codeComplete.localSuffixes=
org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.doc.comment.support=enabled
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
org.eclipse.jdt.core.compiler.problem.deadCode=warning
org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=default
org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags
org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=default
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
org.eclipse.jdt.core.compiler.problem.nullReference=ignore
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.processAnnotations=disabled
org.eclipse.jdt.core.compiler.source=1.6
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
org.eclipse.jdt.core.formatter.comment.format_header=false
org.eclipse.jdt.core.formatter.comment.format_html=true
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
org.eclipse.jdt.core.formatter.comment.format_source_code=false
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=do not insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
org.eclipse.jdt.core.formatter.comment.line_length=90
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
org.eclipse.jdt.core.formatter.compact_else_if=true
org.eclipse.jdt.core.formatter.continuation_indentation=2
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_empty_lines=false
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
org.eclipse.jdt.core.formatter.indentation.size=4
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
org.eclipse.jdt.core.formatter.lineSplit=90
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
org.eclipse.jdt.core.formatter.tabulation.char=tab
org.eclipse.jdt.core.formatter.tabulation.size=4
org.eclipse.jdt.core.formatter.use_on_off_tags=true
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
org.eclipse.jdt.core.javaFormatter=org.springframework.ide.eclipse.jdt.formatter.javaformatter

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,166 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
<!-- Suppressions -->
<module name="SuppressionFilter">
<property name="file" value="${configDir}/suppressions.xml"/>
</module>
<!-- Root Checks -->
<module name="RegexpHeader">
<property name="headerFile" value="${configDir}/header.txt"/>
<property name="fileExtensions" value="java"/>
</module>
<module name="NewlineAtEndOfFile">
<property name="lineSeparator" value="lf"/>
<property name="fileExtensions" value="java,xml"/>
</module>
<!-- TreeWalker Checks -->
<module name="TreeWalker">
<!-- Annotations -->
<module name="AnnotationUseStyle">
<property name="elementStyle" value="compact"/>
</module>
<module name="MissingOverride"/>
<module name="PackageAnnotation"/>
<module name="AnnotationLocation">
<property name="allowSamelineSingleParameterlessAnnotation" value="false" />
</module>
<!-- Block Checks -->
<module name="EmptyBlock">
<property name="option" value="text"/>
</module>
<module name="LeftCurly"/>
<module name="RightCurly">
<property name="option" value="alone"/>
</module>
<module name="NeedBraces"/>
<module name="AvoidNestedBlocks"/>
<!-- Class Design -->
<module name="FinalClass"/>
<module name="InterfaceIsType"/>
<module name="HideUtilityClassConstructor"/>
<module name="MutableException"/>
<module name="InnerTypeLast"/>
<module name="OneTopLevelClass"/>
<!-- Coding -->
<module name="CovariantEquals"/>
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="InnerAssignment"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="StringLiteralEquality"/>
<module name="NestedForDepth">
<property name="max" value="3"/>
</module>
<module name="NestedIfDepth">
<property name="max" value="3"/>
</module>
<module name="NestedTryDepth">
<property name="max" value="3"/>
</module>
<module name="MultipleVariableDeclarations"/>
<module name="RequireThis">
<property name="checkMethods" value="false"/>
</module>
<module name="OneStatementPerLine"/>
<!-- Imports -->
<module name="AvoidStarImport"/>
<module name="AvoidStaticImport">
<property name="excludes"
value="org.assertj.core.api.Assertions.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.AdditionalMatchers.*, org.mockito.ArgumentMatchers.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultHandlers.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*, org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo"/>
</module>
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports">
<property name="processJavadoc" value="true"/>
</module>
<module name="ImportOrder">
<property name="groups" value="java,/^javax?\./,*,org.springframework"/>
<property name="ordered" value="true"/>
<property name="separated" value="true"/>
<property name="option" value="bottom"/>
<property name="sortStaticImportsAlphabetically" value="true"/>
</module>
<!-- Javadoc Comments -->
<module name="JavadocType">
<property name="scope" value="package"/>
<property name="authorFormat" value=".+\s.+"/>
</module>
<module name="JavadocMethod">
<property name="allowMissingJavadoc" value="true"/>
</module>
<module name="JavadocVariable">
<property name="scope" value="public"/>
</module>
<module name="JavadocStyle">
<property name="checkEmptyJavadoc" value="true"/>
</module>
<module name="NonEmptyAtclauseDescription"/>
<module name="JavadocTagContinuationIndentation">
<property name="offset" value="0"/>
</module>
<module name="AtclauseOrder">
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF"/>
<property name="tagOrder" value="@param, @author, @since, @see, @version, @serial, @deprecated"/>
</module>
<module name="AtclauseOrder">
<property name="target" value="METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
<property name="tagOrder" value="@param, @return, @throws, @since, @deprecated, @see"/>
</module>
<!-- Miscellaneous -->
<module name="CommentsIndentation"/>
<module name="UpperEll"/>
<module name="ArrayTypeStyle"/>
<module name="OuterTypeFilename"/>
<!-- Modifiers -->
<module name="RedundantModifier"/>
<!-- Regexp -->
<module name="RegexpSinglelineJava">
<property name="format" value="^\t* +\t*\S"/>
<property name="message" value="Line has leading space characters; indentation should be performed with tabs only."/>
<property name="ignoreComments" value="true"/>
</module>
<module name="RegexpSinglelineJava">
<property name="maximum" value="0"/>
<property name="format" value="org\.mockito\.Mockito\.(when|doThrow|doAnswer)"/>
<property name="message"
value="Please use BDDMockto imports."/>
<property name="ignoreComments" value="true"/>
</module>
<module name="RegexpSinglelineJava">
<property name="maximum" value="0"/>
<property name="format" value="org\.junit\.Assert\.assert"/>
<property name="message" value="Please use AssertJ imports."/>
<property name="ignoreComments" value="true"/>
</module>
<module name="Regexp">
<property name="format" value="[ \t]+$"/>
<property name="illegalPattern" value="true"/>
<property name="message" value="Trailing whitespace"/>
</module>
<!-- Whitespace -->
<module name="GenericWhitespace"/>
<module name="MethodParamPad"/>
<module name="NoWhitespaceAfter">
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, UNARY_PLUS, ARRAY_DECLARATOR"/>
</module>
<module name="NoWhitespaceBefore"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
</module>
</module>

16
etc/checkstyle/header.txt Normal file
View File

@@ -0,0 +1,16 @@
^\Q/*\E$
^\Q * Copyright 2014-\E20\d\d\Q the original author or authors.\E$
^\Q *\E$
^\Q * Licensed under the Apache License, Version 2.0 (the "License");\E$
^\Q * you may not use this file except in compliance with the License.\E$
^\Q * You may obtain a copy of the License at\E$
^\Q *\E$
^\Q * http://www.apache.org/licenses/LICENSE-2.0\E$
^\Q *\E$
^\Q * Unless required by applicable law or agreed to in writing, software\E$
^\Q * distributed under the License is distributed on an "AS IS" BASIS,\E$
^\Q * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\E$
^\Q * See the License for the specific language governing permissions and\E$
^\Q * limitations under the License.\E$
^\Q */\E$
^.*$

View File

@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<suppress files=".+Application\.java" checks="HideUtilityClassConstructor"/>
<suppress files=".+Configuration\.java" checks="HideUtilityClassConstructor"/>
<suppress files=".+SSEFluxWebConfig\.java" checks="RegexpHeader"/>
<suppress files="[\\/]src[\\/]test[\\/]java[\\/]" checks="Javadoc"/>
<suppress files="[\\/]src[\\/]integration-test[\\/]java[\\/]" checks="Javadoc"/>
<suppress files="[\\/]docs[\\/]" checks="Javadoc"/>
<suppress files="[\\/]docs[\\/]" checks="CommentsIndentation"/>
<suppress files="[\\/]docs[\\/]" checks="InnerTypeLast"/>
<suppress files="[\\/]samples[\\/]" checks="Javadoc"/>
<suppress files="[\\/]samples[\\/]" checks="CommentsIndentation"/>
<suppress files="[\\/]samples[\\/]" checks="InnerTypeLast"/>
</suppressions>

View File

@@ -1,14 +1,2 @@
springSecurityVersion=4.0.0.RC1
embeddedRedisVersion=0.2
jstlVersion=1.2.1
jedisVersion=2.4.1
springVersion=4.1.3.RELEASE
groovyVersion=2.3.8
version=1.0.0.RELEASE
seleniumVersion=2.44.0
jacksonVersion=2.4.4
gebVersion=0.10.0
servletApiVersion=3.0.1
spockVersion=0.7-groovy-2.0
commonsPoolVersion=2.2
springDataRedisVersion=1.3.0.RELEASE
springBootVersion=2.0.0.M4
version=2.0.0.M5

View File

@@ -0,0 +1,55 @@
dependencyManagement {
imports {
mavenBom 'com.fasterxml.jackson:jackson-bom:2.9.1'
mavenBom 'io.projectreactor:reactor-bom:Bismuth-RELEASE'
mavenBom 'org.springframework:spring-framework-bom:5.0.0.RELEASE'
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-RELEASE'
mavenBom 'org.springframework.security:spring-security-bom:5.0.0.M5'
}
dependencies {
dependencySet(group: 'com.hazelcast', version: '3.8.6') {
entry 'hazelcast'
entry 'hazelcast-client'
}
dependencySet(group: 'org.testcontainers', version: '1.4.2') {
entry 'mysql'
entry 'postgresql'
entry 'testcontainers'
}
dependency 'ch.qos.logback:logback-classic:1.2.3'
dependency 'com.h2database:h2:1.4.196'
dependency 'com.maxmind.geoip2:geoip2:2.3.1'
dependency 'commons-codec:commons-codec:1.10'
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
dependency 'io.lettuce:lettuce-core:5.0.0.RELEASE'
dependency 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1'
dependency 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.2-b02'
dependency 'javax.servlet:javax.servlet-api:3.1.0'
dependency 'junit:junit:4.12'
dependency 'mysql:mysql-connector-java:5.1.44'
dependency 'org.apache.derby:derby:10.13.1.1'
dependency 'org.apache.httpcomponents:httpclient:4.5.3'
dependency 'org.apache.taglibs:taglibs-standard-jstlel:1.2.5'
dependency 'org.assertj:assertj-core:3.8.0'
dependency 'org.hsqldb:hsqldb:2.4.0'
dependency 'org.mariadb.jdbc:mariadb-java-client:2.1.2'
dependency 'org.mockito:mockito-core:2.10.0'
dependency 'org.postgresql:postgresql:42.1.4'
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.27'
dependency 'org.slf4j:jcl-over-slf4j:1.7.25'
dependency 'org.slf4j:log4j-over-slf4j:1.7.25'
dependency 'org.testcontainers:mariadb:1.3.0'
dependency 'org.thymeleaf.extras:thymeleaf-extras-java8time:3.0.1.RELEASE'
dependency 'org.thymeleaf:thymeleaf-spring5:3.0.7.RC1'
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.0'
dependency 'org.webjars:webjars-taglib:0.3'
}
}

View File

@@ -1,86 +0,0 @@
apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'javadocHotfix'
apply plugin: 'eclipse-wtp'
apply plugin: 'propdeps'
apply plugin: 'propdeps-idea'
apply plugin: 'propdeps-eclipse'
group = 'org.springframework.session'
sourceCompatibility = 1.5
targetCompatibility = 1.5
ext.springIoVersion = project.hasProperty('platformVersion') ? platformVersion : '1.1.0.BUILD-SNAPSHOT'
ext.spockDependencies = [
dependencies.create("org.spockframework:spock-core:$spockVersion") {
exclude group: 'junit', module: 'junit-dep'
}
]
ext.gebDependencies = spockDependencies + [
"org.seleniumhq.selenium:selenium-htmlunit-driver:$seleniumVersion",
"org.gebish:geb-spock:$gebVersion",
'commons-httpclient:commons-httpclient:3.1',
"org.codehaus.groovy:groovy:$groovyVersion"
]
ext.jstlDependencies = [
"javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:$jstlVersion",
"org.apache.taglibs:taglibs-standard-jstlel:1.2.1"
]
repositories {
mavenCentral()
maven { url 'http://clojars.org/repo' }
maven { url 'https://repo.spring.io/libs-snapshot' }
}
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'org.springframework') {
details.useVersion springVersion
}
}
}
// Integration test setup
configurations {
integrationTestCompile {
extendsFrom testCompile, optional, provided
}
integrationTestRuntime {
extendsFrom integrationTestCompile, testRuntime
}
}
sourceSets {
integrationTest {
java.srcDir file('src/integration-test/java')
groovy.srcDirs file('src/integration-test/groovy')
resources.srcDir file('src/integration-test/resources')
compileClasspath = sourceSets.main.output + sourceSets.test.output + configurations.integrationTestCompile
runtimeClasspath = output + compileClasspath + configurations.integrationTestRuntime
}
}
task integrationTest(type: Test, dependsOn: jar) {
testClassesDir = sourceSets.integrationTest.output.classesDir
logging.captureStandardOutput(LogLevel.INFO)
classpath = sourceSets.integrationTest.runtimeClasspath
maxParallelForks = 1
reports {
html.destination = project.file("$project.buildDir/reports/integration-tests/")
junitXml.destination = project.file("$project.buildDir/integration-test-results/")
}
}
eclipse {
classpath {
plusConfigurations += [ configurations.integrationTestCompile ]
}
}
project.idea.module {
scopes.TEST.plus += [project.configurations.integrationTestRuntime]
}

View File

@@ -1,51 +0,0 @@
apply plugin: 'propdeps-maven'
install {
repositories.mavenInstaller {
customizePom(pom, project)
}
}
def customizePom(pom, gradleProject) {
pom.whenConfigured { generatedPom ->
// sort to make pom dependencies order consistent to ease comparison of older poms
generatedPom.dependencies = generatedPom.dependencies.sort { dep ->
"$dep.scope:$dep.groupId:$dep.artifactId"
}
// add all items necessary for maven central publication
generatedPom.project {
name = gradleProject.description
description = gradleProject.description
url = "https://github.com/spring-projects/spring-session"
organization {
name = "Spring IO"
url = "http://projects.spring.io/spring-session"
}
licenses {
license {
name "The Apache Software License, Version 2.0"
url "http://www.apache.org/licenses/LICENSE-2.0.txt"
distribution "repo"
}
}
scm {
url = "https://github.com/spring-projects/spring-session"
connection = "scm:git:git://github.com/spring-projects/spring-session"
developerConnection = "scm:git:git://github.com/spring-projects/spring-session"
}
developers {
developer {
id = "rwinch"
name = "Rob Winch"
email = "rwinch@pivotal.io"
}
}
issueManagement {
system = "GitHub"
url = "https://github.com/spring-projects/spring-session/issues"
}
}
}
}

View File

@@ -1,71 +0,0 @@
buildscript {
repositories {
maven { url "https://repo.spring.io/plugins-release" }
}
dependencies {
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
}
}
apply plugin: 'war'
apply plugin: 'tomcat'
dependencies {
def tomcatVersion = '7.0.54'
tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
"org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"
tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") {
exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj'
}
}
[tomcatRun,tomcatRunWar]*.contextPath = '/'
task integrationTomcatRun(type: org.gradle.api.plugins.tomcat.tasks.TomcatRun) {
onlyIf { !sourceSets.integrationTest.allSource.empty }
buildscriptClasspath = tomcatRun.buildscriptClasspath
contextPath = tomcatRun.contextPath
daemon = true
tomcatClasspath = tomcatRun.tomcatClasspath
webAppClasspath = tomcatRun.webAppClasspath
webAppSourceDirectory = tomcatRun.webAppSourceDirectory
doFirst {
def mainOutputDir = project.sourceSets.main.output.classesDir
if(mainOutputDir) {
classesDirectory = mainOutputDir
}
// delay reserving ports to ensure they are still available
def ports = reservePorts(3)
httpPort = ports[0]
ajpPort = ports[1]
stopPort = ports[2]
}
}
task integrationTomcatStop(type: org.gradle.api.plugins.tomcat.tasks.TomcatStop) {
onlyIf { !sourceSets.integrationTest.allSource.empty }
doFirst {
stopPort = integrationTomcatRun.stopPort
}
}
integrationTest {
dependsOn integrationTomcatRun
doFirst {
def host = 'localhost:' + integrationTomcatRun.httpPort
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/' + integrationTomcatRun.contextPath
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
}
finalizedBy integrationTomcatStop
}
def reservePorts(int count) {
def sockets = []
for(int i in 1..count) {
sockets << new ServerSocket(0)
}
def result = sockets*.localPort
sockets*.close()
result
}

Binary file not shown.

View File

@@ -1,6 +1,5 @@
#Tue Nov 25 20:57:10 CST 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip

110
gradlew vendored
View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
##############################################################################
##
@@ -6,47 +6,6 @@
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -90,7 +89,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -114,6 +113,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@@ -154,11 +154,19 @@ if $cygwin ; then
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
APP_ARGS=$(save "$@")
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

14
gradlew.bat vendored
View File

@@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +46,9 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line

View File

@@ -1,51 +0,0 @@
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-data-redis'),
"org.springframework.boot:spring-boot-starter-web",
"org.springframework.boot:spring-boot-starter-thymeleaf",
"redis.embedded:embedded-redis:0.2",
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
"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
}
}
def reservePort() {
def socket = new ServerSocket(0)
def result = socket.localPort
socket.close()
result
}

View File

@@ -0,0 +1,4 @@
Demonstrates using Spring Session to lookup a user's session by the username.
The sample provides a hook to add the current username to the session (required for finding the user) by providing a custom implementation of Spring Security's `AuthenticationSuccessHandler`.
NOTE: This product includes GeoLite2 data created by MaxMind, available from http://www.maxmind.com

View File

@@ -0,0 +1,28 @@
apply plugin: 'io.spring.convention.spring-sample-boot'
dependencies {
compile project(':spring-session-data-redis')
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
compile "org.springframework.boot:spring-boot-starter-security"
compile "org.springframework.boot:spring-boot-starter-data-redis"
compile "org.springframework.boot:spring-boot-devtools"
compile "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
compile "org.webjars:bootstrap"
compile "org.webjars:html5shiv"
compile "org.webjars:webjars-locator"
compile "com.maxmind.geoip2:geoip2"
compile "org.apache.httpcomponents:httpclient"
testCompile "org.springframework.boot:spring-boot-starter-test"
testCompile "org.assertj:assertj-core"
integrationTestCompile seleniumDependencies
integrationTestCompile "org.testcontainers:testcontainers"
}
integrationTest {
doFirst {
systemProperties['spring.session.redis.namespace'] = project.name
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 2014-2017 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.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.WebDriver;
import org.testcontainers.containers.GenericContainer;
import sample.pages.HomePage;
import sample.pages.LoginPage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder;
/**
* @author Eddú Meléndez
* @author Rob Winch
* @author Vedran Pavic
*/
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
@SpringBootTest(classes = FindByUsernameApplication.class, webEnvironment = WebEnvironment.MOCK)
@ContextConfiguration(initializers = FindByUsernameTests.Initializer.class)
public class FindByUsernameTests {
private static final String DOCKER_IMAGE = "redis:4.0.2";
@ClassRule
public static GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
.withExposedPorts(6379);
@Autowired
private MockMvc mockMvc;
private WebDriver driver;
@Before
public void setup() {
this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).build();
}
@After
public void tearDown() {
this.driver.quit();
}
@Test
public void home() {
LoginPage login = HomePage.go(this.driver);
login.assertAt();
}
@Test
public void login() {
LoginPage login = HomePage.go(this.driver);
HomePage home = login.form().login(HomePage.class);
home.assertAt();
home.containCookie("SESSION");
home.doesNotContainCookie("JSESSIONID");
home.terminateButtonDisabled();
}
static class Initializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(
ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues
.of("spring.redis.host=" + redisContainer.getContainerIpAddress(),
"spring.redis.port=" + redisContainer.getFirstMappedPort())
.applyTo(configurableApplicationContext.getEnvironment());
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2014-2017 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 org.openqa.selenium.WebDriver;
/**
* @author Eddú Meléndez
*/
public class BasePage {
private WebDriver driver;
public BasePage(WebDriver driver) {
this.driver = driver;
}
public WebDriver getDriver() {
return this.driver;
}
public static void get(WebDriver driver, String get) {
String baseUrl = "http://localhost";
driver.get(baseUrl + get);
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2014-2017 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 java.util.Base64;
import java.util.Set;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
* @author Rob Winch
*/
public class HomePage extends BasePage {
@FindBy(css = "input[type=\"submit\"]")
WebElement logout;
public HomePage(WebDriver driver) {
super(driver);
}
public void assertAt() {
assertThat(getDriver().getTitle())
.isEqualTo("Spring Session Sample - Secured Content");
}
public void containCookie(String cookieName) {
Set<Cookie> cookies = getDriver().manage().getCookies();
assertThat(cookies).extracting("name").contains(cookieName);
}
public void doesNotContainCookie(String cookieName) {
Set<Cookie> cookies = getDriver().manage().getCookies();
assertThat(cookies).extracting("name").doesNotContain(cookieName);
}
public void terminateButtonDisabled() {
Set<Cookie> cookies = getDriver().manage().getCookies();
String cookieValue = null;
for (Cookie cookie : cookies) {
if ("SESSION".equals(cookie.getName())) {
cookieValue = new String(Base64.getDecoder().decode(cookie.getValue()));
}
}
WebElement element = getDriver().findElement(By.id("terminate-" + cookieValue));
assertThat(element.isEnabled()).isFalse();
}
public HomePage logout() {
this.logout.click();
return PageFactory.initElements(getDriver(), HomePage.class);
}
public static LoginPage go(WebDriver driver) {
get(driver, "/");
return PageFactory.initElements(driver, LoginPage.class);
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2014-2017 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 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 Eddú Meléndez
* @author Rob Winch
*/
public class LoginPage extends BasePage {
public LoginPage(WebDriver driver) {
super(driver);
}
public void assertAt() {
assertThat(getDriver().getTitle()).isEqualTo("Spring Session Sample - Log In");
}
public Form form() {
return new Form(getDriver());
}
public class Form {
@FindBy(name = "username")
private WebElement username;
@FindBy(name = "password")
private WebElement password;
@FindBy(tagName = "button")
private WebElement button;
public Form(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
public <T> T login(Class<T> page) {
this.username.sendKeys("user");
this.password.sendKeys("password");
this.button.click();
return PageFactory.initElements(getDriver(), page);
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2014-2017 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 FindByUsernameApplication {
public static void main(String[] args) {
SpringApplication.run(FindByUsernameApplication.class, args);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* 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.
@@ -13,27 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.config;
import org.h2.server.web.WebServlet;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import java.io.InputStream;
import com.maxmind.geoip2.DatabaseReader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Initializes the H2 {@link WebServlet} so we can access our in memory database
* from the URL "/h2".
*
* @author Rob Winch
*
*/
@Configuration
public class H2Initializer {
public class GeoConfig {
@Bean
public ServletRegistrationBean h2Servlet() {
ServletRegistrationBean servletBean = new ServletRegistrationBean();
servletBean.addUrlMappings("/h2/*");
servletBean.setServlet(new WebServlet());
return servletBean;
public DatabaseReader geoDatabaseReader(
@Value("classpath:GeoLite2-City.mmdb") InputStream geoInputStream)
throws Exception {
return new DatabaseReader.Builder(geoInputStream).build();
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2014-2017 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.boot.autoconfigure.security.StaticResourceRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
/**
* Spring Security configuration.
*
* @author Rob Winch
* @author Vedran Pavic
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(
User.withUsername("user").password("password").roles("USER").build());
return manager;
}
// @formatter:off
// tag::config[]
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.requestMatchers(StaticResourceRequest.toCommonLocations()).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
// end::config[]
// @formatter:on
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2014-2017 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.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2014-2017 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 java.security.Principal;
import java.util.Collection;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.session.FindByIndexNameSessionRepository;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Controller for sending the user to the login view.
*
* @author Rob Winch
*
*/
@Controller
public class IndexController {
// tag::findbyusername[]
@Autowired
FindByIndexNameSessionRepository<? extends Session> sessions;
@RequestMapping("/")
public String index(Principal principal, Model model) {
Collection<? extends Session> usersSessions = this.sessions
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principal.getName())
.values();
model.addAttribute("sessions", usersSessions);
return "index";
}
// end::findbyusername[]
@RequestMapping(value = "/sessions/{sessionIdToDelete}", method = RequestMethod.DELETE)
public String removeSession(Principal principal,
@PathVariable String sessionIdToDelete) {
Set<String> usersSessionIds = this.sessions.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principal.getName()).keySet();
if (usersSessionIds.contains(sessionIdToDelete)) {
this.sessions.deleteById(sessionIdToDelete);
}
return "redirect:/";
}
}

View File

@@ -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.session;
import java.io.Serializable;
/**
* An example of how users can provide details about their session.
*
* @author Rob Winch
* @see SessionDetailsFilter
*/
// tag::class[]
public class SessionDetails implements Serializable {
private String location;
private String accessType;
public String getLocation() {
return this.location;
}
public void setLocation(String location) {
this.location = location;
}
public String getAccessType() {
return this.accessType;
}
public void setAccessType(String accessType) {
this.accessType = accessType;
}
private static final long serialVersionUID = 8850489178248613501L;
}
// end::class[]

View File

@@ -0,0 +1,111 @@
/*
* 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.session;
import java.io.IOException;
import java.net.InetAddress;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.maxmind.geoip2.DatabaseReader;
import com.maxmind.geoip2.model.CityResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
/**
* Inserts the session details into the session for every request. Some users may prefer
* to insert session details only after authentication. This is fine, but it may be
* valuable to the most up to date information so that if someone stole the user's session
* id it can be observed.
*
* @author Rob Winch
*
*/
// tag::class[]
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 101)
public class SessionDetailsFilter extends OncePerRequestFilter {
static final String UNKNOWN = "Unknown";
private DatabaseReader reader;
@Autowired
public SessionDetailsFilter(DatabaseReader reader) {
this.reader = reader;
}
// tag::dofilterinternal[]
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
HttpSession session = request.getSession(false);
if (session != null) {
String remoteAddr = getRemoteAddress(request);
String geoLocation = getGeoLocation(remoteAddr);
SessionDetails details = new SessionDetails();
details.setAccessType(request.getHeader("User-Agent"));
details.setLocation(remoteAddr + " " + geoLocation);
session.setAttribute("SESSION_DETAILS", details);
}
}
// end::dofilterinternal[]
String getGeoLocation(String remoteAddr) {
try {
CityResponse city = this.reader.city(InetAddress.getByName(remoteAddr));
String cityName = city.getCity().getName();
String countryName = city.getCountry().getName();
if (cityName == null && countryName == null) {
return null;
}
else if (cityName == null) {
return countryName;
}
else if (countryName == null) {
return cityName;
}
return cityName + ", " + countryName;
}
catch (Exception e) {
return UNKNOWN;
}
}
private String getRemoteAddress(HttpServletRequest request) {
String remoteAddr = request.getHeader("X-FORWARDED-FOR");
if (remoteAddr == null) {
remoteAddr = request.getRemoteAddr();
}
else if (remoteAddr.contains(",")) {
remoteAddr = remoteAddr.split(",")[0];
}
return remoteAddr;
}
}
// end::class[]

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 MiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,36 @@
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout}">
<head>
<title>Secured Content</title>
</head>
<body>
<div layout:fragment="content">
<h1>Secured Page</h1>
<p>This page is secured using Spring Boot, Spring Session, and Spring Security.</p>
<p>Your current session id is <span id="session-id" th:text="${#httpSession.id}"></span></p>
<table class="table table-stripped">
<tr>
<th>Id Suffix</th>
<th>Location</th>
<th>Created</th>
<th>Last Updated</th>
<th>Information</th>
<th>Terminate</th>
</tr>
<tr th:each="sessionElement : ${sessions}" th:with="details=${sessionElement.getAttribute('SESSION_DETAILS')}">
<td th:text="${sessionElement.id.substring(30)}"></td>
<td th:text="${details?.location}"></td>
<td th:text="${#temporals.format(sessionElement.creationTime.atZone(T(java.time.ZoneId).systemDefault()),'dd/MMM/yyyy HH:mm:ss')}"></td>
<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="delete">
<input th:id="'terminate-' + ${sessionElement.id}" type="submit" value="Terminate" th:disabled="${sessionElement.id == #httpSession.id}"/>
</form>
</td>
</tr>
</table>
</div>
</body>
</html>

View File

@@ -0,0 +1,122 @@
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">Spring Session Sample</title>
<link rel="icon" type="image/x-icon" th:href="@{/favicon.ico}" href="../static/favicon.ico"/>
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
<style type="text/css">
/* Sticky footer styles
-------------------------------------------------- */
html,
body {
height: 100%;
/* The html and body elements cannot have any padding or margin. */
}
/* Wrapper for page content to push down footer */
#wrap {
min-height: 100%;
height: auto !important;
height: 100%;
/* Negative indent footer by it's height */
margin: 0 auto -60px;
}
/* Set the fixed height of the footer here */
#push,
#footer {
height: 60px;
}
#footer {
background-color: #f5f5f5;
}
/* Lastly, apply responsive CSS fixes as necessary */
@media (max-width: 767px) {
#footer {
margin-left: -20px;
margin-right: -20px;
padding-left: 20px;
padding-right: 20px;
}
}
/* Custom page CSS
-------------------------------------------------- */
/* Not required for template or sticky footer method. */
.container {
width: auto;
max-width: 680px;
}
.container .credit {
margin: 20px 0;
text-align: center;
}
a {
color: green;
}
.navbar-form {
margin-left: 1em;
}
</style>
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
<![endif]-->
</head>
<body>
<div id="wrap">
<div class="navbar navbar-inverse navbar-static-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" th:href="@{/}"><img th:src="@{/images/logo.png}" alt="Spring Security Sample"/></a>
<div class="nav-collapse collapse"
th:with="currentUser=${#httpServletRequest.userPrincipal?.principal}">
<div th:if="${currentUser != null}">
<form class="navbar-form pull-right" th:action="@{/logout}" method="post">
<input type="submit" value="Log out" />
</form>
<p id="un" class="navbar-text pull-right" th:text="${currentUser.username}">
sample_user
</p>
</div>
<ul class="nav">
</ul>
</div>
</div>
</div>
</div>
<!-- Begin page content -->
<div class="container">
<div class="alert alert-success"
th:if="${globalMessage}"
th:text="${globalMessage}">
Some Success message
</div>
<div layout:fragment="content">
Fake content
</div>
</div>
<div id="push"><!-- --></div>
</div>
<div id="footer">
<div class="container">
<p class="muted credit">This product includes GeoLite2 data created by MaxMind, available from <a href="http://www.maxmind.com">http://www.maxmind.com</a>.</p>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,47 @@
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<head>
<title>Log In</title>
</head>
<body>
<div layout:fragment="content">
<form name="f" th:action="@{/login}" method="post">
<fieldset>
<legend>Please Login</legend>
<div th:if="${param.error}" class="alert alert-error">Invalid
username and password.</div>
<div th:if="${param.logout}" class="alert alert-success">You
have been logged out.</div>
<label for="username">Username</label> <input type="text"
id="username" name="username" /> <label for="password">Password</label>
<input type="password" id="password" name="password" />
<div class="form-actions">
<button type="submit" class="btn">Log in</button>
</div>
</fieldset>
</form>
<div class="container">
<p>The intention is to demo a way to terminate a user's active
session without access to the device. Consider the following</p>
<ul>
<li>User goes to library and authenticates to the application</li>
<li>User goes home and realizes they forgot to log out</li>
<li>User can log in and terminate the session from the library
using clues like the location, created time, last accessed time,
etc.</li>
</ul>
<p>To emulate the workflow:</p>
<ul>
<li>Sign in with the <b>username</b> "user" and <b>password</b> "password"</li>
<li>Sign in again using an incognito window</li>
<li>Terminate your original session</li>
<li>Refresh the original window and see you are logged out</li>
</ul>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,66 @@
/*
* 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.session;
import com.maxmind.geoip2.DatabaseReader;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import sample.config.GeoConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = GeoConfig.class)
public class SessionDetailsFilterTests {
@Autowired
DatabaseReader reader;
SessionDetailsFilter filter;
@Before
public void setup() {
this.filter = new SessionDetailsFilter(this.reader);
}
@Test
public void getGeoLocationHanldesInvalidIp() {
assertThat(this.filter.getGeoLocation("a"))
.isEqualTo(SessionDetailsFilter.UNKNOWN);
}
@Test
public void getGeoLocationNullCity() {
assertThat(this.filter.getGeoLocation("22.231.113.64"))
.isEqualTo("United States");
}
@Test
public void getGeoLocationBoth() {
assertThat(this.filter.getGeoLocation("184.154.83.119"))
.isEqualTo("Chicago, United States");
}
}

View File

@@ -0,0 +1,19 @@
apply plugin: 'io.spring.convention.spring-sample-boot'
dependencies {
compile project(':spring-session-jdbc')
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
compile "org.springframework.boot:spring-boot-starter-security"
compile "org.springframework.boot:spring-boot-devtools"
compile "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
compile "org.webjars:bootstrap"
compile "org.webjars:html5shiv"
compile "org.webjars:webjars-locator"
compile "com.h2database:h2"
testCompile "org.springframework.boot:spring-boot-starter-test"
testCompile "org.assertj:assertj-core"
integrationTestCompile seleniumDependencies
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2014-2017 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.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.WebDriver;
import sample.pages.HomePage;
import sample.pages.LoginPage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder;
/**
* @author Eddú Meléndez
*/
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
public class BootTests {
@Autowired
private MockMvc mockMvc;
private WebDriver driver;
@Before
public void setup() {
this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).build();
}
@After
public void tearDown() {
this.driver.quit();
}
@Test
public void home() {
LoginPage login = HomePage.go(this.driver);
login.assertAt();
HomePage home = login.form().login(HomePage.class);
home.assertAt();
}
@Test
public void login() {
LoginPage login = HomePage.go(this.driver);
HomePage home = login.form().login(HomePage.class);
home.containCookie("SESSION");
home.doesNotContainCookie("JSESSIONID");
}
@Test
public void logout() {
LoginPage login = HomePage.go(this.driver);
HomePage home = login.form().login(HomePage.class);
login = home.logout();
login.assertAt();
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2014-2017 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 org.openqa.selenium.WebDriver;
/**
* @author Eddú Meléndez
*/
public class BasePage {
private WebDriver driver;
public BasePage(WebDriver driver) {
this.driver = driver;
}
public WebDriver getDriver() {
return this.driver;
}
public static void get(WebDriver driver, String get) {
String baseUrl = "http://localhost";
driver.get(baseUrl + get);
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2014-2017 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 java.util.Set;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.PageFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
*/
public class HomePage extends BasePage {
public HomePage(WebDriver driver) {
super(driver);
}
public static LoginPage go(WebDriver driver) {
get(driver, "/");
return PageFactory.initElements(driver, LoginPage.class);
}
public void assertAt() {
assertThat(getDriver().getTitle()).isEqualTo("Spring Session Sample - Secured Content");
}
public void containCookie(String cookieName) {
Set<Cookie> cookies = getDriver().manage().getCookies();
assertThat(cookies).extracting("name").contains(cookieName);
}
public void doesNotContainCookie(String cookieName) {
Set<Cookie> cookies = getDriver().manage().getCookies();
assertThat(cookies).extracting("name").doesNotContain(cookieName);
}
public LoginPage logout() {
WebElement logout = getDriver().findElement(By.cssSelector("input[type=\"submit\"]"));
logout.click();
return PageFactory.initElements(getDriver(), LoginPage.class);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2014-2017 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 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 Eddú Meléndez
* @author Rob Winch
*/
public class LoginPage extends BasePage {
public LoginPage(WebDriver driver) {
super(driver);
}
public void assertAt() {
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
}
public Form form() {
return new Form(getDriver());
}
public class Form {
@FindBy(name = "username")
private WebElement username;
@FindBy(name = "password")
private WebElement password;
@FindBy(name = "submit")
private WebElement button;
public Form(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
public <T> T login(Class<T> page) {
this.username.sendKeys("user");
this.password.sendKeys("password");
this.button.click();
return PageFactory.initElements(getDriver(), page);
}
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2014-2017 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.boot.autoconfigure.security.StaticResourceRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
/**
* Spring Security configuration.
*
* @author Rob Winch
* @author Vedran Pavic
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(
User.withUsername("user").password("password").roles("USER").build());
return manager;
}
// @formatter:off
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring().antMatchers("/h2-console/**");
}
// @formatter:on
// @formatter:off
// tag::config[]
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.requestMatchers(StaticResourceRequest.toCommonLocations()).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll();
}
// end::config[]
// @formatter:on
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2014-2017 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.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
}

View File

@@ -0,0 +1 @@
spring.h2.console.enabled=true

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,11 @@
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout}">
<head>
<title>Secured Content</title>
</head>
<body>
<div layout:fragment="content">
<h1>Secured Page</h1>
<p>This page is secured using Spring Boot, Spring Session, and Spring Security.</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,122 @@
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">Spring Session Sample</title>
<link rel="icon" type="image/x-icon" th:href="@{/favicon.ico}" href="../static/favicon.ico"/>
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
<style type="text/css">
/* Sticky footer styles
-------------------------------------------------- */
html,
body {
height: 100%;
/* The html and body elements cannot have any padding or margin. */
}
/* Wrapper for page content to push down footer */
#wrap {
min-height: 100%;
height: auto !important;
height: 100%;
/* Negative indent footer by it's height */
margin: 0 auto -60px;
}
/* Set the fixed height of the footer here */
#push,
#footer {
height: 60px;
}
#footer {
background-color: #f5f5f5;
}
/* Lastly, apply responsive CSS fixes as necessary */
@media (max-width: 767px) {
#footer {
margin-left: -20px;
margin-right: -20px;
padding-left: 20px;
padding-right: 20px;
}
}
/* Custom page CSS
-------------------------------------------------- */
/* Not required for template or sticky footer method. */
.container {
width: auto;
max-width: 680px;
}
.container .credit {
margin: 20px 0;
text-align: center;
}
a {
color: green;
}
.navbar-form {
margin-left: 1em;
}
</style>
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
<![endif]-->
</head>
<body>
<div id="wrap">
<div class="navbar navbar-inverse navbar-static-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" th:href="@{/}"><img th:src="@{/images/logo.png}" alt="Spring Security Sample"/></a>
<div class="nav-collapse collapse"
th:with="currentUser=${#httpServletRequest.userPrincipal?.principal}">
<div th:if="${currentUser != null}">
<form class="navbar-form pull-right" th:action="@{/logout}" method="post">
<input type="submit" value="Log out" />
</form>
<p id="un" class="navbar-text pull-right" th:text="${currentUser.username}">
sample_user
</p>
</div>
<ul class="nav">
</ul>
</div>
</div>
</div>
</div>
<!-- Begin page content -->
<div class="container">
<div class="alert alert-success"
th:if="${globalMessage}"
th:text="${globalMessage}">
Some Success message
</div>
<div layout:fragment="content">
Fake content
</div>
</div>
<div id="push"><!-- --></div>
</div>
<div id="footer">
<div class="container">
<p class="muted credit">Visit the <a href="https://projects.spring.io/spring-session/">Spring Session</a> site for more <a href="https://github.com/spring-projects/spring-session/tree/master/samples">samples</a>.</p>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,22 @@
apply plugin: 'io.spring.convention.spring-sample-boot'
dependencies {
compile project(':spring-session-data-redis')
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
compile "org.springframework.boot:spring-boot-starter-security"
compile "org.springframework.boot:spring-boot-starter-data-redis"
compile "org.springframework.boot:spring-boot-devtools"
compile "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
compile "org.webjars:bootstrap"
compile "org.webjars:html5shiv"
compile "org.webjars:webjars-locator"
compile "org.apache.httpcomponents:httpclient"
testCompile "org.springframework.boot:spring-boot-starter-test"
testCompile "org.assertj:assertj-core"
testCompile "org.skyscreamer:jsonassert"
integrationTestCompile seleniumDependencies
integrationTestCompile "org.testcontainers:testcontainers"
}

Some files were not shown because too many files have changed in this diff Show More