Spaces to tabs and license cleanup
This commit is contained in:
66
build.gradle
66
build.gradle
@@ -1,14 +1,14 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
maven { url "https://repo.spring.io/plugins-release" }
|
maven { url "https://repo.spring.io/plugins-release" }
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
|
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:propdeps-plugin:0.0.7")
|
||||||
classpath("org.springframework.build.gradle:spring-io-plugin:0.0.3.RELEASE")
|
classpath("org.springframework.build.gradle:spring-io-plugin:0.0.3.RELEASE")
|
||||||
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
|
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
|
||||||
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2'
|
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group = 'org.springframework.session'
|
group = 'org.springframework.session'
|
||||||
@@ -27,37 +27,37 @@ apply plugin: 'base'
|
|||||||
|
|
||||||
|
|
||||||
sonarRunner {
|
sonarRunner {
|
||||||
sonarProperties {
|
sonarProperties {
|
||||||
property "sonar.java.coveragePlugin", "jacoco"
|
property "sonar.java.coveragePlugin", "jacoco"
|
||||||
property "sonar.projectName", "Spring Session"
|
property "sonar.projectName", "Spring Session"
|
||||||
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
|
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
|
||||||
property "sonar.links.homepage", 'https://github.com/spring-projects/spring-session'
|
property "sonar.links.homepage", 'https://github.com/spring-projects/spring-session'
|
||||||
property "sonar.links.ci", 'https://build.spring.io/browse/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.issue", 'https://github.com/spring-projects/spring-session/issues'
|
||||||
property "sonar.links.scm", 'https://github.com/spring-projects/spring-session'
|
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.links.scm_dev", 'https://github.com/spring-projects/spring-session.git'
|
||||||
property "sonar.java.coveragePlugin", "jacoco"
|
property "sonar.java.coveragePlugin", "jacoco"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task configDocsZip(dependsOn: [':docs:asciidoctor',':spring-session:javadoc']) << {
|
task configDocsZip(dependsOn: [':docs:asciidoctor',':spring-session:javadoc']) << {
|
||||||
project.tasks.docsZip.from(project(':docs').asciidoctor) {
|
project.tasks.docsZip.from(project(':docs').asciidoctor) {
|
||||||
into('reference')
|
into('reference')
|
||||||
}
|
}
|
||||||
project.tasks.docsZip.from(project(':spring-session').javadoc) {
|
project.tasks.docsZip.from(project(':spring-session').javadoc) {
|
||||||
into('api')
|
into('api')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
task docsZip(type: Zip, dependsOn: 'configDocsZip') {
|
task docsZip(type: Zip, dependsOn: 'configDocsZip') {
|
||||||
group = "Distribution"
|
group = "Distribution"
|
||||||
baseName = "spring-session"
|
baseName = "spring-session"
|
||||||
classifier = "docs"
|
classifier = "docs"
|
||||||
description = "Builds -${classifier} archive containing api and reference " +
|
description = "Builds -${classifier} archive containing api and reference " +
|
||||||
"for deployment."
|
"for deployment."
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
archives docsZip
|
archives docsZip
|
||||||
}
|
}
|
||||||
@@ -8,37 +8,37 @@ asciidoctorj {
|
|||||||
tasks.findByPath("artifactoryPublish")?.enabled = false
|
tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testCompile project(':spring-session'),
|
testCompile project(':spring-session'),
|
||||||
"org.springframework.data:spring-data-redis:$springDataRedisVersion",
|
"org.springframework.data:spring-data-redis:$springDataRedisVersion",
|
||||||
"org.springframework:spring-websocket:${springVersion}",
|
"org.springframework:spring-websocket:${springVersion}",
|
||||||
"org.springframework:spring-messaging:${springVersion}",
|
"org.springframework:spring-messaging:${springVersion}",
|
||||||
'junit:junit:4.11',
|
'junit:junit:4.11',
|
||||||
'org.mockito:mockito-core:1.9.5',
|
'org.mockito:mockito-core:1.9.5',
|
||||||
"org.springframework:spring-test:$springVersion",
|
"org.springframework:spring-test:$springVersion",
|
||||||
'org.easytesting:fest-assert:1.4',
|
'org.easytesting:fest-assert:1.4',
|
||||||
"redis.clients:jedis:2.4.1"
|
"redis.clients:jedis:2.4.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
asciidoctor {
|
asciidoctor {
|
||||||
def ghTag = snapshotBuild ? 'master' : project.version
|
def ghTag = snapshotBuild ? 'master' : project.version
|
||||||
def ghUrl = "https://github.com/spring-projects/spring-session/tree/$ghTag/"
|
def ghUrl = "https://github.com/spring-projects/spring-session/tree/$ghTag/"
|
||||||
attributes 'version-snapshot': snapshotBuild,
|
attributes 'version-snapshot': snapshotBuild,
|
||||||
'version-milestone': milestoneBuild,
|
'version-milestone': milestoneBuild,
|
||||||
'version-release': releaseBuild,
|
'version-release': releaseBuild,
|
||||||
'gh-url': ghUrl,
|
'gh-url': ghUrl,
|
||||||
'gh-samples-url': "$ghUrl/samples/",
|
'gh-samples-url': "$ghUrl/samples/",
|
||||||
'download-url' : "https://github.com/spring-projects/spring-session/archive/${ghTag}.zip",
|
'download-url' : "https://github.com/spring-projects/spring-session/archive/${ghTag}.zip",
|
||||||
'spring-session-version' : version,
|
'spring-session-version' : version,
|
||||||
'spring-version' : springVersion,
|
'spring-version' : springVersion,
|
||||||
'docs-test-dir' : rootProject.projectDir.path + '/docs/src/test/java/',
|
'docs-test-dir' : rootProject.projectDir.path + '/docs/src/test/java/',
|
||||||
'samples-dir' : rootProject.projectDir.path + '/samples/',
|
'samples-dir' : rootProject.projectDir.path + '/samples/',
|
||||||
|
|
||||||
'source-highlighter' : 'coderay',
|
'source-highlighter' : 'coderay',
|
||||||
'imagesdir':'./images',
|
'imagesdir':'./images',
|
||||||
'icons': 'font',
|
'icons': 'font',
|
||||||
'sectanchors':'',
|
'sectanchors':'',
|
||||||
'idprefix':'',
|
'idprefix':'',
|
||||||
'idseparator':'-',
|
'idseparator':'-',
|
||||||
'docinfo1':'true',
|
'docinfo1':'true',
|
||||||
'revnumber' : project.version
|
'revnumber' : project.version
|
||||||
}
|
}
|
||||||
@@ -16,14 +16,14 @@ If you are using Maven, ensure to add the following dependencies:
|
|||||||
[subs="verbatim,attributes"]
|
[subs="verbatim,attributes"]
|
||||||
----
|
----
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.session</groupId>
|
<groupId>org.springframework.session</groupId>
|
||||||
<artifactId>spring-session-data-redis</artifactId>
|
<artifactId>spring-session-data-redis</artifactId>
|
||||||
<version>{spring-session-version}</version>
|
<version>{spring-session-version}</version>
|
||||||
<type>pom<type>
|
<type>pom<type>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -36,12 +36,12 @@ Ensure you have the following in your pom.xml:
|
|||||||
----
|
----
|
||||||
<repositories>
|
<repositories>
|
||||||
|
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
|
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-snapshot</id>
|
<id>spring-snapshot</id>
|
||||||
<url>https://repo.spring.io/libs-snapshot</url>
|
<url>https://repo.spring.io/libs-snapshot</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
----
|
----
|
||||||
endif::[]
|
endif::[]
|
||||||
@@ -54,8 +54,8 @@ Ensure you have the following in your pom.xml:
|
|||||||
[source,xml]
|
[source,xml]
|
||||||
----
|
----
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-milestone</id>
|
<id>spring-milestone</id>
|
||||||
<url>https://repo.spring.io/libs-milestone</url>
|
<url>https://repo.spring.io/libs-milestone</url>
|
||||||
</repository>
|
</repository>
|
||||||
----
|
----
|
||||||
endif::[]
|
endif::[]
|
||||||
@@ -69,7 +69,7 @@ Add the following Spring Configuration:
|
|||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
include::{samples-dir}boot/src/main/java/sample/config/HttpSessionConfig.java[]
|
include::{samples-dir}boot/src/main/java/sample/config/HttpSessionConfig.java[tags=class]
|
||||||
----
|
----
|
||||||
|
|
||||||
<1> 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.
|
||||||
@@ -111,7 +111,9 @@ The boot Sample Application demonstrates how to use Spring Session to transparen
|
|||||||
|
|
||||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||||
|
|
||||||
$ ./gradlew :samples:boot:bootRun
|
----
|
||||||
|
$ ./gradlew :samples:boot:bootRun
|
||||||
|
----
|
||||||
|
|
||||||
You should now be able to access the application at http://localhost:8080/
|
You should now be able to access the application at http://localhost:8080/
|
||||||
|
|
||||||
@@ -139,12 +141,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:
|
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].
|
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:
|
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.
|
||||||
|
|||||||
@@ -15,19 +15,19 @@ If you are using Maven, ensure to add the following dependencies:
|
|||||||
[subs="verbatim,attributes"]
|
[subs="verbatim,attributes"]
|
||||||
----
|
----
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.session</groupId>
|
<groupId>org.springframework.session</groupId>
|
||||||
<artifactId>spring-session-data-redis</artifactId>
|
<artifactId>spring-session-data-redis</artifactId>
|
||||||
<version>{spring-session-version}</version>
|
<version>{spring-session-version}</version>
|
||||||
<type>pom<type>
|
<type>pom<type>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-web</artifactId>
|
<artifactId>spring-web</artifactId>
|
||||||
<version>{spring-version}</version>
|
<version>{spring-version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -40,12 +40,12 @@ Ensure you have the following in your pom.xml:
|
|||||||
----
|
----
|
||||||
<repositories>
|
<repositories>
|
||||||
|
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
|
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-snapshot</id>
|
<id>spring-snapshot</id>
|
||||||
<url>https://repo.spring.io/libs-snapshot</url>
|
<url>https://repo.spring.io/libs-snapshot</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
----
|
----
|
||||||
endif::[]
|
endif::[]
|
||||||
@@ -58,8 +58,8 @@ Ensure you have the following in your pom.xml:
|
|||||||
[source,xml]
|
[source,xml]
|
||||||
----
|
----
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-milestone</id>
|
<id>spring-milestone</id>
|
||||||
<url>https://repo.spring.io/libs-milestone</url>
|
<url>https://repo.spring.io/libs-milestone</url>
|
||||||
</repository>
|
</repository>
|
||||||
----
|
----
|
||||||
endif::[]
|
endif::[]
|
||||||
@@ -75,7 +75,7 @@ Add the following Spring Configuration:
|
|||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
include::{samples-dir}httpsession/src/main/java/sample/Config.java[]
|
include::{samples-dir}httpsession/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.
|
<1> We import an embedded Redis Server so that there is no need to start up Redis external of our application.
|
||||||
@@ -100,7 +100,7 @@ You can find an example below:
|
|||||||
.src/main/java/sample/Initializer.java
|
.src/main/java/sample/Initializer.java
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
include::{samples-dir}httpsession/src/main/java/sample/Initializer.java[]
|
include::{samples-dir}httpsession/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`.
|
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||||
@@ -120,7 +120,9 @@ 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:
|
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||||
|
|
||||||
$ ./gradlew :samples:httpsession:tomcatRun
|
----
|
||||||
|
$ ./gradlew :samples:httpsession:tomcatRun
|
||||||
|
----
|
||||||
|
|
||||||
You should now be able to access the application at http://localhost:8080/
|
You should now be able to access the application at http://localhost:8080/
|
||||||
|
|
||||||
@@ -140,7 +142,7 @@ We interact with the standard `HttpSession` in the `SessionServlet` shown below:
|
|||||||
.src/main/java/sample/SessionServlet.java
|
.src/main/java/sample/SessionServlet.java
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
include::{samples-dir}httpsession/src/main/java/sample/SessionServlet.java[]
|
include::{samples-dir}httpsession/src/main/java/sample/SessionServlet.java[tags=class]
|
||||||
----
|
----
|
||||||
|
|
||||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
|
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
|
||||||
@@ -149,12 +151,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:
|
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].
|
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:
|
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.
|
||||||
@@ -15,19 +15,19 @@ If you are using Maven, ensure to add the following dependencies:
|
|||||||
[subs="verbatim,attributes"]
|
[subs="verbatim,attributes"]
|
||||||
----
|
----
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.session</groupId>
|
<groupId>org.springframework.session</groupId>
|
||||||
<artifactId>spring-session-data-redis</artifactId>
|
<artifactId>spring-session-data-redis</artifactId>
|
||||||
<version>{spring-session-version}</version>
|
<version>{spring-session-version}</version>
|
||||||
<type>pom<type>
|
<type>pom<type>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-web</artifactId>
|
<artifactId>spring-web</artifactId>
|
||||||
<version>{spring-version}</version>
|
<version>{spring-version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -40,12 +40,12 @@ Ensure you have the following in your pom.xml:
|
|||||||
----
|
----
|
||||||
<repositories>
|
<repositories>
|
||||||
|
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
|
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-snapshot</id>
|
<id>spring-snapshot</id>
|
||||||
<url>https://repo.spring.io/libs-snapshot</url>
|
<url>https://repo.spring.io/libs-snapshot</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
----
|
----
|
||||||
endif::[]
|
endif::[]
|
||||||
@@ -58,8 +58,8 @@ Ensure you have the following in your pom.xml:
|
|||||||
[source,xml]
|
[source,xml]
|
||||||
----
|
----
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-milestone</id>
|
<id>spring-milestone</id>
|
||||||
<url>https://repo.spring.io/libs-milestone</url>
|
<url>https://repo.spring.io/libs-milestone</url>
|
||||||
</repository>
|
</repository>
|
||||||
----
|
----
|
||||||
endif::[]
|
endif::[]
|
||||||
@@ -75,7 +75,7 @@ Add the following Spring Configuration:
|
|||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
include::{samples-dir}rest/src/main/java/sample/HttpSessionConfig.java[]
|
include::{samples-dir}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.
|
<1> We import an embedded Redis Server so that there is no need to start up Redis external of our application.
|
||||||
@@ -107,7 +107,7 @@ Fortunately, Spring Session provides a utility class named `AbstractHttpSessionA
|
|||||||
.src/main/java/sample/Initializer.java
|
.src/main/java/sample/Initializer.java
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
include::{samples-dir}rest/src/main/java/sample/Initializer.java[]
|
include::{samples-dir}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`.
|
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||||
@@ -121,7 +121,9 @@ 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:
|
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||||
|
|
||||||
$ ./gradlew :samples:rest:tomcatRun
|
----
|
||||||
|
$ ./gradlew :samples:rest:tomcatRun
|
||||||
|
----
|
||||||
|
|
||||||
You should now be able to access the application at http://localhost:8080/
|
You should now be able to access the application at http://localhost:8080/
|
||||||
|
|
||||||
@@ -129,14 +131,14 @@ 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/
|
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:
|
Observe that we are prompted for basic authentication. Provide the following information for the username and password:
|
||||||
|
|
||||||
* **Username** *user*
|
* **Username** *user*
|
||||||
* **Password** *password*
|
* **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:
|
In the output you will notice the following:
|
||||||
|
|
||||||
@@ -156,13 +158,13 @@ Specifically, we notice the following things about our response:
|
|||||||
|
|
||||||
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 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.
|
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.
|
||||||
|
|
||||||
@@ -181,7 +183,7 @@ Spring Session creates a header named x-auth-token in your browser that contains
|
|||||||
|
|
||||||
If you like, you can easily see that the session is created in Redis. First create a session using the following:
|
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:
|
In the output you will notice the following:
|
||||||
|
|
||||||
@@ -195,14 +197,14 @@ x-auth-token: 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
|||||||
|
|
||||||
Now remove the session using redis-cli. For example, on a Linux based system you can type:
|
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].
|
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:
|
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"
|
||||||
|
|||||||
@@ -16,19 +16,19 @@ If you are using Maven, ensure to add the following dependencies:
|
|||||||
[subs="verbatim,attributes"]
|
[subs="verbatim,attributes"]
|
||||||
----
|
----
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.session</groupId>
|
<groupId>org.springframework.session</groupId>
|
||||||
<artifactId>spring-session-data-redis</artifactId>
|
<artifactId>spring-session-data-redis</artifactId>
|
||||||
<version>{spring-session-version}</version>
|
<version>{spring-session-version}</version>
|
||||||
<type>pom<type>
|
<type>pom<type>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-web</artifactId>
|
<artifactId>spring-web</artifactId>
|
||||||
<version>{spring-version}</version>
|
<version>{spring-version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -41,12 +41,12 @@ Ensure you have the following in your pom.xml:
|
|||||||
----
|
----
|
||||||
<repositories>
|
<repositories>
|
||||||
|
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
|
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-snapshot</id>
|
<id>spring-snapshot</id>
|
||||||
<url>https://repo.spring.io/libs-snapshot</url>
|
<url>https://repo.spring.io/libs-snapshot</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
----
|
----
|
||||||
endif::[]
|
endif::[]
|
||||||
@@ -59,8 +59,8 @@ Ensure you have the following in your pom.xml:
|
|||||||
[source,xml]
|
[source,xml]
|
||||||
----
|
----
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-milestone</id>
|
<id>spring-milestone</id>
|
||||||
<url>https://repo.spring.io/libs-milestone</url>
|
<url>https://repo.spring.io/libs-milestone</url>
|
||||||
</repository>
|
</repository>
|
||||||
----
|
----
|
||||||
endif::[]
|
endif::[]
|
||||||
@@ -74,7 +74,7 @@ Add the following Spring Configuration:
|
|||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
include::{samples-dir}security/src/main/java/sample/Config.java[]
|
include::{samples-dir}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.
|
<1> We import an embedded Redis Server so that there is no need to start up Redis external of our application.
|
||||||
@@ -97,7 +97,7 @@ Since our application is already loading Spring configuration using our `Securit
|
|||||||
.src/main/java/sample/SecurityInitializer.java
|
.src/main/java/sample/SecurityInitializer.java
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
include::{samples-dir}security/src/main/java/sample/SecurityInitializer.java[]
|
include::{samples-dir}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.
|
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||||
@@ -109,7 +109,7 @@ You can find an example below:
|
|||||||
.src/main/java/sample/Initializer.java
|
.src/main/java/sample/Initializer.java
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
include::{samples-dir}security/src/main/java/sample/Initializer.java[]
|
include::{samples-dir}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`.
|
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||||
@@ -125,7 +125,9 @@ 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:
|
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||||
|
|
||||||
$ ./gradlew :samples:security:tomcatRun
|
----
|
||||||
|
$ ./gradlew :samples:security:tomcatRun
|
||||||
|
----
|
||||||
|
|
||||||
You should now be able to access the application at http://localhost:8080/
|
You should now be able to access the application at http://localhost:8080/
|
||||||
|
|
||||||
@@ -151,12 +153,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:
|
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].
|
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:
|
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.
|
||||||
@@ -17,7 +17,9 @@ 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:
|
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||||
|
|
||||||
$ ./gradlew :samples:users:tomcatRun
|
----
|
||||||
|
$ ./gradlew :samples:users:tomcatRun
|
||||||
|
----
|
||||||
|
|
||||||
You should now be able to access the application at http://localhost:8080/
|
You should now be able to access the application at http://localhost:8080/
|
||||||
|
|
||||||
@@ -71,7 +73,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.
|
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:
|
For example, the SESSION cookie might have a value of:
|
||||||
|
|
||||||
7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||||
|
|
||||||
=== Adding a Session
|
=== Adding a Session
|
||||||
|
|
||||||
@@ -113,7 +115,7 @@ The URL contains a session alias that either points to an existing unauthenticat
|
|||||||
|
|
||||||
Now our SESSION cookie looks something like this:
|
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:
|
Such that:
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,9 @@ include::{samples-dir}websocket/src/main/java/sample/config/WebSecurityConfig.ja
|
|||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
$ ./gradlew :samples:websocket:bootRun
|
----
|
||||||
|
$ ./gradlew :samples:websocket:bootRun
|
||||||
|
----
|
||||||
|
|
||||||
You should now be able to access the application at http://localhost:8080/
|
You should now be able to access the application at http://localhost:8080/
|
||||||
|
|
||||||
|
|||||||
@@ -106,19 +106,19 @@ It looks something like the following:
|
|||||||
----
|
----
|
||||||
public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
|
public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
|
||||||
|
|
||||||
public SessionRepositoryRequestWrapper(HttpServletRequest original) {
|
public SessionRepositoryRequestWrapper(HttpServletRequest original) {
|
||||||
super(original);
|
super(original);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpSession getSession() {
|
public HttpSession getSession() {
|
||||||
return getSession(true);
|
return getSession(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpSession getSession(boolean createNew) {
|
public HttpSession getSession(boolean createNew) {
|
||||||
// create an HttpSession implementation from Spring Session
|
// create an HttpSession implementation from Spring Session
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... other methods delegate to the original HttpServletRequest ...
|
// ... other methods delegate to the original HttpServletRequest ...
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -132,15 +132,15 @@ The pseudocode can be found below:
|
|||||||
----
|
----
|
||||||
public class SessionRepositoryFilter implements Filter {
|
public class SessionRepositoryFilter implements Filter {
|
||||||
|
|
||||||
public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
|
public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
|
||||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||||
SessionRepositoryRequestWrapper customRequest =
|
SessionRepositoryRequestWrapper customRequest =
|
||||||
new SessionRepositoryRequestWrapper(httpRequest);
|
new SessionRepositoryRequestWrapper(httpRequest);
|
||||||
|
|
||||||
chain.doFilter(customRequest, response, chain);
|
chain.doFilter(customRequest, response, chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -286,9 +286,9 @@ Each session is stored in Redis as a Hash.
|
|||||||
Each session is set and updated using the HMSET command.
|
Each session is set and updated using the HMSET command.
|
||||||
An example of how each session is stored can be seen below.
|
An example of how each session is stored can be seen below.
|
||||||
|
|
||||||
HMSET spring:session:sessions:<session-id> creationTime 1404360000000 \
|
HMSET spring:session:sessions:<session-id> creationTime 1404360000000 \
|
||||||
maxInactiveInterval 1800 lastAccessedTime 1404360000000 \
|
maxInactiveInterval 1800 lastAccessedTime 1404360000000 \
|
||||||
sessionAttr:<attrName> someAttrValue sessionAttr:<attrName2> someAttrValue2
|
sessionAttr:<attrName> someAttrValue sessionAttr:<attrName2> someAttrValue2
|
||||||
|
|
||||||
[[api-redisoperationssessionrepository-expiration]]
|
[[api-redisoperationssessionrepository-expiration]]
|
||||||
===== Session Expiration
|
===== Session Expiration
|
||||||
@@ -296,7 +296,7 @@ An example of how each session is stored can be seen below.
|
|||||||
An expiration is associated to each session using the EXPIRE command based upon the RedisOperationsSessionRepository.RedisSession.getMaxInactiveInterval().
|
An expiration is associated to each session using the EXPIRE command based upon the RedisOperationsSessionRepository.RedisSession.getMaxInactiveInterval().
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
EXPIRE spring:session:sessions:<session-id> 1800
|
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>>.
|
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.
|
It is the `SessionDestroyedEvent` that ensures resources associated with the Session are cleaned up.
|
||||||
@@ -313,8 +313,8 @@ 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.
|
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:
|
For example:
|
||||||
|
|
||||||
SADD spring:session:expirations:<expire-rounded-up-to-nearest-minute> <session-id>
|
SADD spring:session:expirations:<expire-rounded-up-to-nearest-minute> <session-id>
|
||||||
EXPIRE spring:session:expirations:<expire-rounded-up-to-nearest-minute> 1800
|
EXPIRE spring:session:expirations:<expire-rounded-up-to-nearest-minute> 1800
|
||||||
|
|
||||||
The background task will then use these mappings to explicitly request each key.
|
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.
|
By accessing they key, rather than deleting it, we ensure that Redis deletes the key for us only if the TTL is expired.
|
||||||
@@ -331,8 +331,8 @@ 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.
|
For example, assume the session attribute "sessionAttr2" from earlier was updated.
|
||||||
The following would be executed upon saving:
|
The following would be executed upon saving:
|
||||||
|
|
||||||
HMSET spring:session:sessions:<session-id> sessionAttr:<attrName2> newValue
|
HMSET spring:session:sessions:<session-id> sessionAttr:<attrName2> newValue
|
||||||
EXPIRE spring:session:sessions:<session-id> 1800
|
EXPIRE spring:session:sessions:<session-id> 1800
|
||||||
|
|
||||||
[[api-redisoperationssessionrepository-sessiondestroyedevent]]
|
[[api-redisoperationssessionrepository-sessiondestroyedevent]]
|
||||||
==== SessionDestroyedEvent
|
==== SessionDestroyedEvent
|
||||||
@@ -405,7 +405,7 @@ include::{indexdoc-tests}[tags=new-mapsessionrepository]
|
|||||||
The <<samples,Hazelcast Sample>> is a complete application demonstrating using Spring Session with Hazelcast.
|
The <<samples,Hazelcast Sample>> is a complete application demonstrating using Spring Session with Hazelcast.
|
||||||
To run it use the following:
|
To run it use the following:
|
||||||
|
|
||||||
./gradlew :samples:hazelcast:tomcatRun
|
./gradlew :samples:hazelcast:tomcatRun
|
||||||
|
|
||||||
[[community]]
|
[[community]]
|
||||||
== Spring Session Community
|
== Spring Session Community
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -26,85 +26,85 @@ import static org.fest.assertions.Assertions.assertThat;
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
public class IndexDocTests {
|
public class IndexDocTests {
|
||||||
static final String ATTR_USER = "user";
|
static final String ATTR_USER = "user";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void repositoryDemo() {
|
public void repositoryDemo() {
|
||||||
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
|
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
|
||||||
demo.repository = new MapSessionRepository();
|
demo.repository = new MapSessionRepository();
|
||||||
|
|
||||||
demo.demo();
|
demo.demo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// tag::repository-demo[]
|
// tag::repository-demo[]
|
||||||
public class RepositoryDemo<S extends Session> {
|
public class RepositoryDemo<S extends Session> {
|
||||||
private SessionRepository<S> repository; // <1>
|
private SessionRepository<S> repository; // <1>
|
||||||
|
|
||||||
public void demo() {
|
public void demo() {
|
||||||
S toSave = repository.createSession(); // <2>
|
S toSave = repository.createSession(); // <2>
|
||||||
|
|
||||||
// <3>
|
// <3>
|
||||||
User rwinch = new User("rwinch");
|
User rwinch = new User("rwinch");
|
||||||
toSave.setAttribute(ATTR_USER, rwinch);
|
toSave.setAttribute(ATTR_USER, rwinch);
|
||||||
|
|
||||||
repository.save(toSave); // <4>
|
repository.save(toSave); // <4>
|
||||||
|
|
||||||
S session = repository.getSession(toSave.getId()); // <5>
|
S session = repository.getSession(toSave.getId()); // <5>
|
||||||
|
|
||||||
// <6>
|
// <6>
|
||||||
User user = session.getAttribute(ATTR_USER);
|
User user = session.getAttribute(ATTR_USER);
|
||||||
assertThat(user).isEqualTo(rwinch);
|
assertThat(user).isEqualTo(rwinch);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... setter methods ...
|
// ... setter methods ...
|
||||||
}
|
}
|
||||||
// end::repository-demo[]
|
// end::repository-demo[]
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void expireRepositoryDemo() {
|
public void expireRepositoryDemo() {
|
||||||
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
|
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
|
||||||
demo.repository = new MapSessionRepository();
|
demo.repository = new MapSessionRepository();
|
||||||
|
|
||||||
demo.demo();
|
demo.demo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// tag::expire-repository-demo[]
|
// tag::expire-repository-demo[]
|
||||||
public class ExpiringRepositoryDemo<S extends ExpiringSession> {
|
public class ExpiringRepositoryDemo<S extends ExpiringSession> {
|
||||||
private SessionRepository<S> repository; // <1>
|
private SessionRepository<S> repository; // <1>
|
||||||
|
|
||||||
public void demo() {
|
public void demo() {
|
||||||
S toSave = repository.createSession(); // <2>
|
S toSave = repository.createSession(); // <2>
|
||||||
// ...
|
// ...
|
||||||
toSave.setMaxInactiveIntervalInSeconds(30); // <3>
|
toSave.setMaxInactiveIntervalInSeconds(30); // <3>
|
||||||
|
|
||||||
repository.save(toSave); // <4>
|
repository.save(toSave); // <4>
|
||||||
|
|
||||||
S session = repository.getSession(toSave.getId()); // <5>
|
S session = repository.getSession(toSave.getId()); // <5>
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... setter methods ...
|
// ... setter methods ...
|
||||||
}
|
}
|
||||||
// end::expire-repository-demo[]
|
// end::expire-repository-demo[]
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void newRedisOperationsSessionRepository() {
|
public void newRedisOperationsSessionRepository() {
|
||||||
// tag::new-redisoperationssessionrepository[]
|
// tag::new-redisoperationssessionrepository[]
|
||||||
JedisConnectionFactory factory = new JedisConnectionFactory();
|
JedisConnectionFactory factory = new JedisConnectionFactory();
|
||||||
SessionRepository<? extends ExpiringSession> repository =
|
SessionRepository<? extends ExpiringSession> repository =
|
||||||
new RedisOperationsSessionRepository(factory);
|
new RedisOperationsSessionRepository(factory);
|
||||||
// end::new-redisoperationssessionrepository[]
|
// end::new-redisoperationssessionrepository[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mapRepository() {
|
public void mapRepository() {
|
||||||
// tag::new-mapsessionrepository[]
|
// tag::new-mapsessionrepository[]
|
||||||
SessionRepository<? extends ExpiringSession> repository = new MapSessionRepository();
|
SessionRepository<? extends ExpiringSession> repository = new MapSessionRepository();
|
||||||
// end::new-mapsessionrepository[]
|
// end::new-mapsessionrepository[]
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class User {
|
private static class User {
|
||||||
private User(String username) {}
|
private User(String username) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -30,17 +30,17 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
|||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@EnableWebSocketMessageBroker
|
@EnableWebSocketMessageBroker
|
||||||
public class WebSocketConfig
|
public class WebSocketConfig
|
||||||
extends AbstractWebSocketMessageBrokerConfigurer {
|
extends AbstractWebSocketMessageBrokerConfigurer {
|
||||||
|
|
||||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||||
registry.addEndpoint("/messages")
|
registry.addEndpoint("/messages")
|
||||||
.withSockJS();
|
.withSockJS();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||||
registry.enableSimpleBroker("/queue/", "/topic/");
|
registry.enableSimpleBroker("/queue/", "/topic/");
|
||||||
registry.setApplicationDestinationPrefixes("/app");
|
registry.setApplicationDestinationPrefixes("/app");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// end::class[]
|
// end::class[]
|
||||||
@@ -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 {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -14,73 +14,73 @@ targetCompatibility = 1.5
|
|||||||
ext.springIoVersion = project.hasProperty('platformVersion') ? platformVersion : '1.1.0.BUILD-SNAPSHOT'
|
ext.springIoVersion = project.hasProperty('platformVersion') ? platformVersion : '1.1.0.BUILD-SNAPSHOT'
|
||||||
|
|
||||||
ext.spockDependencies = [
|
ext.spockDependencies = [
|
||||||
dependencies.create("org.spockframework:spock-core:$spockVersion") {
|
dependencies.create("org.spockframework:spock-core:$spockVersion") {
|
||||||
exclude group: 'junit', module: 'junit-dep'
|
exclude group: 'junit', module: 'junit-dep'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
ext.gebDependencies = spockDependencies + [
|
ext.gebDependencies = spockDependencies + [
|
||||||
"org.seleniumhq.selenium:selenium-htmlunit-driver:$seleniumVersion",
|
"org.seleniumhq.selenium:selenium-htmlunit-driver:$seleniumVersion",
|
||||||
"org.gebish:geb-spock:$gebVersion",
|
"org.gebish:geb-spock:$gebVersion",
|
||||||
'commons-httpclient:commons-httpclient:3.1',
|
'commons-httpclient:commons-httpclient:3.1',
|
||||||
"org.codehaus.groovy:groovy:$groovyVersion"
|
"org.codehaus.groovy:groovy:$groovyVersion"
|
||||||
]
|
]
|
||||||
|
|
||||||
ext.jstlDependencies = [
|
ext.jstlDependencies = [
|
||||||
"javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:$jstlVersion",
|
"javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:$jstlVersion",
|
||||||
"org.apache.taglibs:taglibs-standard-jstlel:1.2.1"
|
"org.apache.taglibs:taglibs-standard-jstlel:1.2.1"
|
||||||
]
|
]
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url 'http://clojars.org/repo' }
|
maven { url 'http://clojars.org/repo' }
|
||||||
maven { url 'https://repo.spring.io/libs-snapshot' }
|
maven { url 'https://repo.spring.io/libs-snapshot' }
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations.all {
|
configurations.all {
|
||||||
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
|
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
|
||||||
if (details.requested.group == 'org.springframework') {
|
if (details.requested.group == 'org.springframework') {
|
||||||
details.useVersion springVersion
|
details.useVersion springVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Integration test setup
|
// Integration test setup
|
||||||
configurations {
|
configurations {
|
||||||
integrationTestCompile {
|
integrationTestCompile {
|
||||||
extendsFrom testCompile, optional, provided
|
extendsFrom testCompile, optional, provided
|
||||||
}
|
}
|
||||||
integrationTestRuntime {
|
integrationTestRuntime {
|
||||||
extendsFrom integrationTestCompile, testRuntime
|
extendsFrom integrationTestCompile, testRuntime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
integrationTest {
|
integrationTest {
|
||||||
java.srcDir file('src/integration-test/java')
|
java.srcDir file('src/integration-test/java')
|
||||||
groovy.srcDirs file('src/integration-test/groovy')
|
groovy.srcDirs file('src/integration-test/groovy')
|
||||||
resources.srcDir file('src/integration-test/resources')
|
resources.srcDir file('src/integration-test/resources')
|
||||||
compileClasspath = sourceSets.main.output + sourceSets.test.output + configurations.integrationTestCompile
|
compileClasspath = sourceSets.main.output + sourceSets.test.output + configurations.integrationTestCompile
|
||||||
runtimeClasspath = output + compileClasspath + configurations.integrationTestRuntime
|
runtimeClasspath = output + compileClasspath + configurations.integrationTestRuntime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task integrationTest(type: Test, dependsOn: jar) {
|
task integrationTest(type: Test, dependsOn: jar) {
|
||||||
testClassesDir = sourceSets.integrationTest.output.classesDir
|
testClassesDir = sourceSets.integrationTest.output.classesDir
|
||||||
logging.captureStandardOutput(LogLevel.INFO)
|
logging.captureStandardOutput(LogLevel.INFO)
|
||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
maxParallelForks = 1
|
maxParallelForks = 1
|
||||||
reports {
|
reports {
|
||||||
html.destination = project.file("$project.buildDir/reports/integration-tests/")
|
html.destination = project.file("$project.buildDir/reports/integration-tests/")
|
||||||
junitXml.destination = project.file("$project.buildDir/integration-test-results/")
|
junitXml.destination = project.file("$project.buildDir/integration-test-results/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eclipse {
|
eclipse {
|
||||||
classpath {
|
classpath {
|
||||||
plusConfigurations += [ configurations.integrationTestCompile ]
|
plusConfigurations += [ configurations.integrationTestCompile ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
project.idea.module {
|
project.idea.module {
|
||||||
scopes.TEST.plus += [project.configurations.integrationTestRuntime]
|
scopes.TEST.plus += [project.configurations.integrationTestRuntime]
|
||||||
}
|
}
|
||||||
@@ -1,51 +1,51 @@
|
|||||||
apply plugin: 'propdeps-maven'
|
apply plugin: 'propdeps-maven'
|
||||||
|
|
||||||
install {
|
install {
|
||||||
repositories.mavenInstaller {
|
repositories.mavenInstaller {
|
||||||
customizePom(pom, project)
|
customizePom(pom, project)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def customizePom(pom, gradleProject) {
|
def customizePom(pom, gradleProject) {
|
||||||
pom.whenConfigured { generatedPom ->
|
pom.whenConfigured { generatedPom ->
|
||||||
|
|
||||||
// sort to make pom dependencies order consistent to ease comparison of older poms
|
// sort to make pom dependencies order consistent to ease comparison of older poms
|
||||||
generatedPom.dependencies = generatedPom.dependencies.sort { dep ->
|
generatedPom.dependencies = generatedPom.dependencies.sort { dep ->
|
||||||
"$dep.scope:$dep.groupId:$dep.artifactId"
|
"$dep.scope:$dep.groupId:$dep.artifactId"
|
||||||
}
|
}
|
||||||
|
|
||||||
// add all items necessary for maven central publication
|
// add all items necessary for maven central publication
|
||||||
generatedPom.project {
|
generatedPom.project {
|
||||||
name = gradleProject.description
|
name = gradleProject.description
|
||||||
description = gradleProject.description
|
description = gradleProject.description
|
||||||
url = "https://github.com/spring-projects/spring-session"
|
url = "https://github.com/spring-projects/spring-session"
|
||||||
organization {
|
organization {
|
||||||
name = "Spring IO"
|
name = "Spring IO"
|
||||||
url = "http://projects.spring.io/spring-session"
|
url = "http://projects.spring.io/spring-session"
|
||||||
}
|
}
|
||||||
licenses {
|
licenses {
|
||||||
license {
|
license {
|
||||||
name "The Apache Software License, Version 2.0"
|
name "The Apache Software License, Version 2.0"
|
||||||
url "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
url "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
distribution "repo"
|
distribution "repo"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scm {
|
scm {
|
||||||
url = "https://github.com/spring-projects/spring-session"
|
url = "https://github.com/spring-projects/spring-session"
|
||||||
connection = "scm:git:git://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"
|
developerConnection = "scm:git:git://github.com/spring-projects/spring-session"
|
||||||
}
|
}
|
||||||
developers {
|
developers {
|
||||||
developer {
|
developer {
|
||||||
id = "rwinch"
|
id = "rwinch"
|
||||||
name = "Rob Winch"
|
name = "Rob Winch"
|
||||||
email = "rwinch@pivotal.io"
|
email = "rwinch@pivotal.io"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
issueManagement {
|
issueManagement {
|
||||||
system = "GitHub"
|
system = "GitHub"
|
||||||
url = "https://github.com/spring-projects/spring-session/issues"
|
url = "https://github.com/spring-projects/spring-session/issues"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,71 +1,71 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
maven { url "https://repo.spring.io/plugins-release" }
|
maven { url "https://repo.spring.io/plugins-release" }
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
|
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'war'
|
apply plugin: 'war'
|
||||||
apply plugin: 'tomcat'
|
apply plugin: 'tomcat'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
def tomcatVersion = '7.0.54'
|
def tomcatVersion = '7.0.54'
|
||||||
tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
|
tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
|
||||||
"org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"
|
"org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"
|
||||||
tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") {
|
tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") {
|
||||||
exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj'
|
exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[tomcatRun,tomcatRunWar]*.contextPath = '/'
|
[tomcatRun,tomcatRunWar]*.contextPath = '/'
|
||||||
|
|
||||||
|
|
||||||
task integrationTomcatRun(type: org.gradle.api.plugins.tomcat.tasks.TomcatRun) {
|
task integrationTomcatRun(type: org.gradle.api.plugins.tomcat.tasks.TomcatRun) {
|
||||||
onlyIf { !sourceSets.integrationTest.allSource.empty }
|
onlyIf { !sourceSets.integrationTest.allSource.empty }
|
||||||
buildscriptClasspath = tomcatRun.buildscriptClasspath
|
buildscriptClasspath = tomcatRun.buildscriptClasspath
|
||||||
contextPath = tomcatRun.contextPath
|
contextPath = tomcatRun.contextPath
|
||||||
daemon = true
|
daemon = true
|
||||||
tomcatClasspath = tomcatRun.tomcatClasspath
|
tomcatClasspath = tomcatRun.tomcatClasspath
|
||||||
webAppClasspath = tomcatRun.webAppClasspath
|
webAppClasspath = tomcatRun.webAppClasspath
|
||||||
webAppSourceDirectory = tomcatRun.webAppSourceDirectory
|
webAppSourceDirectory = tomcatRun.webAppSourceDirectory
|
||||||
doFirst {
|
doFirst {
|
||||||
def mainOutputDir = project.sourceSets.main.output.classesDir
|
def mainOutputDir = project.sourceSets.main.output.classesDir
|
||||||
if(mainOutputDir) {
|
if(mainOutputDir) {
|
||||||
classesDirectory = mainOutputDir
|
classesDirectory = mainOutputDir
|
||||||
}
|
}
|
||||||
// delay reserving ports to ensure they are still available
|
// delay reserving ports to ensure they are still available
|
||||||
def ports = reservePorts(3)
|
def ports = reservePorts(3)
|
||||||
httpPort = ports[0]
|
httpPort = ports[0]
|
||||||
ajpPort = ports[1]
|
ajpPort = ports[1]
|
||||||
stopPort = ports[2]
|
stopPort = ports[2]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task integrationTomcatStop(type: org.gradle.api.plugins.tomcat.tasks.TomcatStop) {
|
task integrationTomcatStop(type: org.gradle.api.plugins.tomcat.tasks.TomcatStop) {
|
||||||
onlyIf { !sourceSets.integrationTest.allSource.empty }
|
onlyIf { !sourceSets.integrationTest.allSource.empty }
|
||||||
doFirst {
|
doFirst {
|
||||||
stopPort = integrationTomcatRun.stopPort
|
stopPort = integrationTomcatRun.stopPort
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
integrationTest {
|
integrationTest {
|
||||||
dependsOn integrationTomcatRun
|
dependsOn integrationTomcatRun
|
||||||
doFirst {
|
doFirst {
|
||||||
def host = 'localhost:' + integrationTomcatRun.httpPort
|
def host = 'localhost:' + integrationTomcatRun.httpPort
|
||||||
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/' + integrationTomcatRun.contextPath
|
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/' + integrationTomcatRun.contextPath
|
||||||
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
|
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
|
||||||
}
|
}
|
||||||
finalizedBy integrationTomcatStop
|
finalizedBy integrationTomcatStop
|
||||||
}
|
}
|
||||||
|
|
||||||
def reservePorts(int count) {
|
def reservePorts(int count) {
|
||||||
def sockets = []
|
def sockets = []
|
||||||
for(int i in 1..count) {
|
for(int i in 1..count) {
|
||||||
sockets << new ServerSocket(0)
|
sockets << new ServerSocket(0)
|
||||||
}
|
}
|
||||||
def result = sockets*.localPort
|
def result = sockets*.localPort
|
||||||
sockets*.close()
|
sockets*.close()
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
|
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'spring-boot'
|
apply plugin: 'spring-boot'
|
||||||
@@ -16,36 +16,36 @@ tasks.findByPath("artifactoryPublish")?.enabled = false
|
|||||||
group = 'samples'
|
group = 'samples'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':spring-session-data-redis'),
|
compile project(':spring-session-data-redis'),
|
||||||
"org.springframework.boot:spring-boot-starter-web",
|
"org.springframework.boot:spring-boot-starter-web",
|
||||||
"org.springframework.boot:spring-boot-starter-thymeleaf",
|
"org.springframework.boot:spring-boot-starter-thymeleaf",
|
||||||
"redis.embedded:embedded-redis:0.2",
|
"redis.embedded:embedded-redis:0.2",
|
||||||
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
|
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
|
||||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||||
"org.springframework.security:spring-security-config:$springSecurityVersion"
|
"org.springframework.security:spring-security-config:$springSecurityVersion"
|
||||||
|
|
||||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||||
|
|
||||||
integrationTestCompile gebDependencies,
|
integrationTestCompile gebDependencies,
|
||||||
"org.spockframework:spock-spring:$spockVersion"
|
"org.spockframework:spock-spring:$spockVersion"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
integrationTest {
|
integrationTest {
|
||||||
doFirst {
|
doFirst {
|
||||||
def port = reservePort()
|
def port = reservePort()
|
||||||
|
|
||||||
def host = 'localhost:' + port
|
def host = 'localhost:' + port
|
||||||
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/'
|
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/'
|
||||||
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
|
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
|
||||||
systemProperties['server.port'] = port
|
systemProperties['server.port'] = port
|
||||||
systemProperties['management.port'] = 0
|
systemProperties['management.port'] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def reservePort() {
|
def reservePort() {
|
||||||
def socket = new ServerSocket(0)
|
def socket = new ServerSocket(0)
|
||||||
def result = socket.localPort
|
def result = socket.localPort
|
||||||
socket.close()
|
socket.close()
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample
|
package sample
|
||||||
|
|
||||||
import geb.spock.*
|
import geb.spock.*
|
||||||
@@ -23,37 +38,37 @@ import pages.*
|
|||||||
@IntegrationTest
|
@IntegrationTest
|
||||||
class BootTests extends GebReportingSpec {
|
class BootTests extends GebReportingSpec {
|
||||||
|
|
||||||
def 'Unauthenticated user sent to log in page'() {
|
def 'Unauthenticated user sent to log in page'() {
|
||||||
when: 'unauthenticated user request protected page'
|
when: 'unauthenticated user request protected page'
|
||||||
via HomePage
|
via HomePage
|
||||||
then: 'sent to the log in page'
|
then: 'sent to the log in page'
|
||||||
at LoginPage
|
at LoginPage
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'Log in views home page'() {
|
def 'Log in views home page'() {
|
||||||
when: 'log in successfully'
|
when: 'log in successfully'
|
||||||
login()
|
login()
|
||||||
then: 'sent to original page'
|
then: 'sent to original page'
|
||||||
at HomePage
|
at HomePage
|
||||||
and: 'the username is displayed'
|
and: 'the username is displayed'
|
||||||
username == 'user'
|
username == 'user'
|
||||||
and: 'Spring Session Management is being used'
|
and: 'Spring Session Management is being used'
|
||||||
driver.manage().cookies.find { it.name == 'SESSION' }
|
driver.manage().cookies.find { it.name == 'SESSION' }
|
||||||
and: 'Standard Session is NOT being used'
|
and: 'Standard Session is NOT being used'
|
||||||
!driver.manage().cookies.find { it.name == 'JSESSIONID' }
|
!driver.manage().cookies.find { it.name == 'JSESSIONID' }
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'Log out success'() {
|
def 'Log out success'() {
|
||||||
when:
|
when:
|
||||||
logout()
|
logout()
|
||||||
then:
|
then:
|
||||||
at LoginPage
|
at LoginPage
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'Logged out user sent to log in page'() {
|
def 'Logged out user sent to log in page'() {
|
||||||
when: 'logged out user request protected page'
|
when: 'logged out user request protected page'
|
||||||
via HomePage
|
via HomePage
|
||||||
then: 'sent to the log in page'
|
then: 'sent to the log in page'
|
||||||
at LoginPage
|
at LoginPage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.pages
|
package sample.pages
|
||||||
|
|
||||||
import geb.*
|
import geb.*
|
||||||
@@ -8,10 +23,10 @@ import geb.*
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
class HomePage extends Page {
|
class HomePage extends Page {
|
||||||
static url = ''
|
static url = ''
|
||||||
static at = { assert driver.title == 'Spring Session Sample - Secured Content'; true}
|
static at = { assert driver.title == 'Spring Session Sample - Secured Content'; true}
|
||||||
static content = {
|
static content = {
|
||||||
username { $('#un').text() }
|
username { $('#un').text() }
|
||||||
logout(to:LoginPage) { $('input[type=submit]').click() }
|
logout(to:LoginPage) { $('input[type=submit]').click() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.pages
|
package sample.pages
|
||||||
|
|
||||||
import geb.*
|
import geb.*
|
||||||
@@ -8,15 +23,15 @@ import geb.*
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
class LoginPage extends Page {
|
class LoginPage extends Page {
|
||||||
static url = '/login'
|
static url = '/login'
|
||||||
static at = { assert driver.title == 'Login Page'; true}
|
static at = { assert driver.title == 'Login Page'; true}
|
||||||
static content = {
|
static content = {
|
||||||
form { $('form') }
|
form { $('form') }
|
||||||
submit { $('input[type=submit]') }
|
submit { $('input[type=submit]') }
|
||||||
login(required:false) { user='user', pass='password' ->
|
login(required:false) { user='user', pass='password' ->
|
||||||
form.username = user
|
form.username = user
|
||||||
form.password = pass
|
form.password = pass
|
||||||
submit.click(HomePage)
|
submit.click(HomePage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -28,7 +28,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
public class Application {
|
public class Application {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(Application.class, args);
|
SpringApplication.run(Application.class, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
package sample.config;
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
* you may not use this file except in compliance with the License.
|
||||||
* the License at
|
* 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
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* License for the specific language governing permissions and limitations under
|
* See the License for the specific language governing permissions and
|
||||||
* the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package sample.config;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
@@ -36,35 +36,33 @@ import redis.embedded.RedisServer;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class EmbeddedRedisConfiguration {
|
public class EmbeddedRedisConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public static RedisServerBean redisServer() {
|
public static RedisServerBean redisServer() {
|
||||||
return new RedisServerBean();
|
return new RedisServerBean();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
||||||
* is initialized before any other Beans. Specifically, we want to ensure
|
* is initialized before any other Beans. Specifically, we want to ensure
|
||||||
* that the Redis Server is started before RedisHttpSessionConfiguration
|
* that the Redis Server is started before RedisHttpSessionConfiguration
|
||||||
* attempts to enable Keyspace notifications.
|
* attempts to enable Keyspace notifications.
|
||||||
*/
|
*/
|
||||||
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
||||||
private RedisServer redisServer;
|
private RedisServer redisServer;
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
||||||
redisServer.start();
|
redisServer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() throws Exception {
|
public void destroy() throws Exception {
|
||||||
if(redisServer != null) {
|
if(redisServer != null) {
|
||||||
redisServer.stop();
|
redisServer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
||||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
|
||||||
|
|
||||||
@Override
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
||||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.config;
|
package sample.config;
|
||||||
|
|
||||||
import org.springframework.session.data.redis.config.annotation.web.http.*;
|
import org.springframework.session.data.redis.config.annotation.web.http.*;
|
||||||
|
|
||||||
|
// tag::class[]
|
||||||
@EnableRedisHttpSession // <1>
|
@EnableRedisHttpSession // <1>
|
||||||
public class HttpSessionConfig { }
|
public class HttpSessionConfig { }
|
||||||
|
// end::class[]
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -26,10 +26,10 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
|||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
auth
|
auth
|
||||||
.inMemoryAuthentication()
|
.inMemoryAuthentication()
|
||||||
.withUser("user").password("password").roles("USER");
|
.withUser("user").password("password").roles("USER");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -26,8 +26,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
public class IndexController {
|
public class IndexController {
|
||||||
@RequestMapping("/")
|
@RequestMapping("/")
|
||||||
public String index() {
|
public String index() {
|
||||||
return "index";
|
return "index";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="layout">
|
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="layout">
|
||||||
<head>
|
<head>
|
||||||
<title>Secured Content</title>
|
<title>Secured Content</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div layout:fragment="content">
|
<div layout:fragment="content">
|
||||||
<h1>Secured Page</h1>
|
<h1>Secured Page</h1>
|
||||||
<p>This page is secured using Spring Boot, Spring Session, and Spring Security.</p>
|
<p>This page is secured using Spring Boot, Spring Session, and Spring Security.</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,122 +1,122 @@
|
|||||||
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd">
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd">
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
xmlns:th="http://www.thymeleaf.org"
|
xmlns:th="http://www.thymeleaf.org"
|
||||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
||||||
<head>
|
<head>
|
||||||
<title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">Spring Session Sample</title>
|
<title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">Spring Session Sample</title>
|
||||||
<link rel="icon" type="image/x-icon" th:href="@{/resources/img/favicon.ico}" href="../static/img/favicon.ico"/>
|
<link rel="icon" type="image/x-icon" th:href="@{/resources/img/favicon.ico}" href="../static/img/favicon.ico"/>
|
||||||
<link th:href="@{/resources/css/bootstrap.css}" href="../static/css/bootstrap.css" rel="stylesheet"></link>
|
<link th:href="@{/resources/css/bootstrap.css}" href="../static/css/bootstrap.css" rel="stylesheet"></link>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
/* Sticky footer styles
|
/* Sticky footer styles
|
||||||
-------------------------------------------------- */
|
-------------------------------------------------- */
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
/* The html and body elements cannot have any padding or margin. */
|
/* The html and body elements cannot have any padding or margin. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wrapper for page content to push down footer */
|
/* Wrapper for page content to push down footer */
|
||||||
#wrap {
|
#wrap {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
/* Negative indent footer by it's height */
|
/* Negative indent footer by it's height */
|
||||||
margin: 0 auto -60px;
|
margin: 0 auto -60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the fixed height of the footer here */
|
/* Set the fixed height of the footer here */
|
||||||
#push,
|
#push,
|
||||||
#footer {
|
#footer {
|
||||||
height: 60px;
|
height: 60px;
|
||||||
}
|
}
|
||||||
#footer {
|
#footer {
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lastly, apply responsive CSS fixes as necessary */
|
/* Lastly, apply responsive CSS fixes as necessary */
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
#footer {
|
#footer {
|
||||||
margin-left: -20px;
|
margin-left: -20px;
|
||||||
margin-right: -20px;
|
margin-right: -20px;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Custom page CSS
|
/* Custom page CSS
|
||||||
-------------------------------------------------- */
|
-------------------------------------------------- */
|
||||||
/* Not required for template or sticky footer method. */
|
/* Not required for template or sticky footer method. */
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
width: auto;
|
width: auto;
|
||||||
max-width: 680px;
|
max-width: 680px;
|
||||||
}
|
}
|
||||||
.container .credit {
|
.container .credit {
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
color: green;
|
color: green;
|
||||||
}
|
}
|
||||||
.navbar-form {
|
.navbar-form {
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<link th:href="@{resources/css/bootstrap-responsive.css}" href="/static/css/bootstrap-responsive.css" rel="stylesheet"></link>
|
<link th:href="@{resources/css/bootstrap-responsive.css}" href="/static/css/bootstrap-responsive.css" rel="stylesheet"></link>
|
||||||
|
|
||||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="wrap">
|
<div id="wrap">
|
||||||
<div class="navbar navbar-inverse navbar-static-top">
|
<div class="navbar navbar-inverse navbar-static-top">
|
||||||
<div class="navbar-inner">
|
<div class="navbar-inner">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="brand" th:href="@{/}"><img th:src="@{/resources/img/logo.png}" alt="Spring Security Sample"/></a>
|
<a class="brand" th:href="@{/}"><img th:src="@{/resources/img/logo.png}" alt="Spring Security Sample"/></a>
|
||||||
|
|
||||||
<div class="nav-collapse collapse"
|
<div class="nav-collapse collapse"
|
||||||
th:with="currentUser=${#httpServletRequest.userPrincipal?.principal}">
|
th:with="currentUser=${#httpServletRequest.userPrincipal?.principal}">
|
||||||
<div th:if="${currentUser != null}">
|
<div th:if="${currentUser != null}">
|
||||||
<form class="navbar-form pull-right" th:action="@{/logout}" method="post">
|
<form class="navbar-form pull-right" th:action="@{/logout}" method="post">
|
||||||
<input type="submit" value="Log out" />
|
<input type="submit" value="Log out" />
|
||||||
</form>
|
</form>
|
||||||
<p id="un" class="navbar-text pull-right" th:text="${currentUser.username}">
|
<p id="un" class="navbar-text pull-right" th:text="${currentUser.username}">
|
||||||
sample_user
|
sample_user
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Begin page content -->
|
<!-- Begin page content -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="alert alert-success"
|
<div class="alert alert-success"
|
||||||
th:if="${globalMessage}"
|
th:if="${globalMessage}"
|
||||||
th:text="${globalMessage}">
|
th:text="${globalMessage}">
|
||||||
Some Success message
|
Some Success message
|
||||||
</div>
|
</div>
|
||||||
<div layout:fragment="content">
|
<div layout:fragment="content">
|
||||||
Fake content
|
Fake content
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="push"><!-- --></div>
|
<div id="push"><!-- --></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="muted credit">Visit the <a href="http://spring.io/spring-security">Spring Security</a> site for more <a href="https://github.com/spring-projects/spring-security/blob/master/samples/">samples</a>.</p>
|
<p class="muted credit">Visit the <a href="http://spring.io/spring-security">Spring Security</a> site for more <a href="https://github.com/spring-projects/spring-security/blob/master/samples/">samples</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -3,17 +3,17 @@ apply from: TOMCAT_GRADLE
|
|||||||
|
|
||||||
tasks.findByPath("artifactoryPublish")?.enabled = false
|
tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||||
sonarRunner {
|
sonarRunner {
|
||||||
skipProject = true
|
skipProject = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':spring-session'),
|
compile project(':spring-session'),
|
||||||
"com.hazelcast:hazelcast-client:3.3.3",
|
"com.hazelcast:hazelcast-client:3.3.3",
|
||||||
jstlDependencies
|
jstlDependencies
|
||||||
|
|
||||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||||
|
|
||||||
testCompile 'junit:junit:4.11'
|
testCompile 'junit:junit:4.11'
|
||||||
|
|
||||||
integrationTestCompile gebDependencies
|
integrationTestCompile gebDependencies
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample
|
package sample
|
||||||
|
|
||||||
import geb.spock.*
|
import geb.spock.*
|
||||||
@@ -12,19 +27,19 @@ import pages.*
|
|||||||
*/
|
*/
|
||||||
@Stepwise
|
@Stepwise
|
||||||
class AttributeTests extends GebReportingSpec {
|
class AttributeTests extends GebReportingSpec {
|
||||||
def 'first visit no attributes'() {
|
def 'first visit no attributes'() {
|
||||||
when:
|
when:
|
||||||
to HomePage
|
to HomePage
|
||||||
then:
|
then:
|
||||||
attributes.empty
|
attributes.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'create attribute'() {
|
def 'create attribute'() {
|
||||||
when:
|
when:
|
||||||
createAttribute('a','b')
|
createAttribute('a','b')
|
||||||
then:
|
then:
|
||||||
attributes.size() == 1
|
attributes.size() == 1
|
||||||
attributes[0].name == 'a'
|
attributes[0].name == 'a'
|
||||||
attributes[0].value == 'b'
|
attributes[0].value == 'b'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.pages
|
package sample.pages
|
||||||
|
|
||||||
import geb.*
|
import geb.*
|
||||||
@@ -8,23 +23,23 @@ import geb.*
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
class HomePage extends Page {
|
class HomePage extends Page {
|
||||||
static url = ''
|
static url = ''
|
||||||
static at = { assert driver.title == 'Session Attributes'; true}
|
static at = { assert driver.title == 'Session Attributes'; true}
|
||||||
static content = {
|
static content = {
|
||||||
form { $('form') }
|
form { $('form') }
|
||||||
submit { $('input[type=submit]') }
|
submit { $('input[type=submit]') }
|
||||||
createAttribute(required:false) { name, value ->
|
createAttribute(required:false) { name, value ->
|
||||||
form.attributeName = name
|
form.attributeName = name
|
||||||
form.attributeValue = value
|
form.attributeValue = value
|
||||||
submit.click(HomePage)
|
submit.click(HomePage)
|
||||||
}
|
}
|
||||||
attributes { moduleList AttributeRow, $("table tr").tail() }
|
attributes { moduleList AttributeRow, $("table tr").tail() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class AttributeRow extends Module {
|
class AttributeRow extends Module {
|
||||||
static content = {
|
static content = {
|
||||||
cell { $("td", it) }
|
cell { $("td", it) }
|
||||||
name { cell(0).text() }
|
name { cell(0).text() }
|
||||||
value { cell(1).text() }
|
value { cell(1).text() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -40,51 +40,51 @@ import java.util.Map;
|
|||||||
|
|
||||||
@WebListener
|
@WebListener
|
||||||
public class Initializer implements ServletContextListener {
|
public class Initializer implements ServletContextListener {
|
||||||
private HazelcastInstance instance;
|
private HazelcastInstance instance;
|
||||||
|
|
||||||
public void contextInitialized(ServletContextEvent sce) {
|
public void contextInitialized(ServletContextEvent sce) {
|
||||||
String sessionMapName = "spring:session:sessions";
|
String sessionMapName = "spring:session:sessions";
|
||||||
ServletContext sc = sce.getServletContext();
|
ServletContext sc = sce.getServletContext();
|
||||||
|
|
||||||
Config cfg = new Config();
|
Config cfg = new Config();
|
||||||
NetworkConfig netConfig = new NetworkConfig();
|
NetworkConfig netConfig = new NetworkConfig();
|
||||||
netConfig.setPort(getAvailablePort());
|
netConfig.setPort(getAvailablePort());
|
||||||
cfg.setNetworkConfig(netConfig);
|
cfg.setNetworkConfig(netConfig);
|
||||||
SerializerConfig serializer = new SerializerConfig()
|
SerializerConfig serializer = new SerializerConfig()
|
||||||
.setTypeClass(Object.class)
|
.setTypeClass(Object.class)
|
||||||
.setImplementation(new ObjectStreamSerializer());
|
.setImplementation(new ObjectStreamSerializer());
|
||||||
cfg.getSerializationConfig().addSerializerConfig(serializer);
|
cfg.getSerializationConfig().addSerializerConfig(serializer);
|
||||||
MapConfig mc = new MapConfig();
|
MapConfig mc = new MapConfig();
|
||||||
mc.setName(sessionMapName);
|
mc.setName(sessionMapName);
|
||||||
mc.setTimeToLiveSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
mc.setTimeToLiveSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||||
cfg.addMapConfig(mc);
|
cfg.addMapConfig(mc);
|
||||||
|
|
||||||
instance = Hazelcast.newHazelcastInstance(cfg);
|
instance = Hazelcast.newHazelcastInstance(cfg);
|
||||||
Map<String,ExpiringSession> sessions = instance.getMap(sessionMapName);
|
Map<String,ExpiringSession> sessions = instance.getMap(sessionMapName);
|
||||||
|
|
||||||
SessionRepository<ExpiringSession> sessionRepository =
|
SessionRepository<ExpiringSession> sessionRepository =
|
||||||
new MapSessionRepository(sessions);
|
new MapSessionRepository(sessions);
|
||||||
SessionRepositoryFilter<ExpiringSession> filter =
|
SessionRepositoryFilter<ExpiringSession> filter =
|
||||||
new SessionRepositoryFilter<ExpiringSession>(sessionRepository);
|
new SessionRepositoryFilter<ExpiringSession>(sessionRepository);
|
||||||
Dynamic fr = sc.addFilter("springSessionFilter", filter);
|
Dynamic fr = sc.addFilter("springSessionFilter", filter);
|
||||||
fr.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
|
fr.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void contextDestroyed(ServletContextEvent sce) {
|
public void contextDestroyed(ServletContextEvent sce) {
|
||||||
instance.shutdown();
|
instance.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getAvailablePort() {
|
private static int getAvailablePort() {
|
||||||
ServerSocket socket = null;
|
ServerSocket socket = null;
|
||||||
try {
|
try {
|
||||||
socket = new ServerSocket(0);
|
socket = new ServerSocket(0);
|
||||||
return socket.getLocalPort();
|
return socket.getLocalPort();
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
socket.close();
|
socket.close();
|
||||||
}catch(IOException e) {}
|
}catch(IOException e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -32,28 +32,28 @@ import com.hazelcast.nio.serialization.StreamSerializer;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ObjectStreamSerializer implements StreamSerializer<Object> {
|
public class ObjectStreamSerializer implements StreamSerializer<Object> {
|
||||||
public int getTypeId() {
|
public int getTypeId() {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(ObjectDataOutput objectDataOutput, Object object)
|
public void write(ObjectDataOutput objectDataOutput, Object object)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
ObjectOutputStream out = new ObjectOutputStream((OutputStream) objectDataOutput);
|
ObjectOutputStream out = new ObjectOutputStream((OutputStream) objectDataOutput);
|
||||||
out.writeObject(object);
|
out.writeObject(object);
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object read(ObjectDataInput objectDataInput)
|
public Object read(ObjectDataInput objectDataInput)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
ObjectInputStream in = new ObjectInputStream((InputStream) objectDataInput);
|
ObjectInputStream in = new ObjectInputStream((InputStream) objectDataInput);
|
||||||
try {
|
try {
|
||||||
return in.readObject();
|
return in.readObject();
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
package sample;
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
* you may not use this file except in compliance with the License.
|
||||||
* the License at
|
* 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
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* License for the specific language governing permissions and limitations under
|
* See the License for the specific language governing permissions and
|
||||||
* the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package sample;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.annotation.WebServlet;
|
import javax.servlet.annotation.WebServlet;
|
||||||
@@ -28,13 +28,13 @@ import java.io.IOException;
|
|||||||
@WebServlet("/session")
|
@WebServlet("/session")
|
||||||
public class SessionServlet extends HttpServlet {
|
public class SessionServlet extends HttpServlet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
String attributeName = req.getParameter("attributeName");
|
String attributeName = req.getParameter("attributeName");
|
||||||
String attributeValue = req.getParameter("attributeValue");
|
String attributeValue = req.getParameter("attributeValue");
|
||||||
req.getSession().setAttribute(attributeName, attributeValue);
|
req.getSession().setAttribute(attributeName, attributeValue);
|
||||||
resp.sendRedirect(req.getContextPath() + "/");
|
resp.sendRedirect(req.getContextPath() + "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final long serialVersionUID = 2878267318695777395L;
|
private static final long serialVersionUID = 2878267318695777395L;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,18 @@ apply from: TOMCAT_GRADLE
|
|||||||
|
|
||||||
tasks.findByPath("artifactoryPublish")?.enabled = false
|
tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||||
sonarRunner {
|
sonarRunner {
|
||||||
skipProject = true
|
skipProject = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':spring-session-data-redis'),
|
compile project(':spring-session-data-redis'),
|
||||||
"org.springframework:spring-web:$springVersion",
|
"org.springframework:spring-web:$springVersion",
|
||||||
"redis.embedded:embedded-redis:0.2",
|
"redis.embedded:embedded-redis:0.2",
|
||||||
jstlDependencies
|
jstlDependencies
|
||||||
|
|
||||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||||
|
|
||||||
testCompile 'junit:junit:4.11'
|
testCompile 'junit:junit:4.11'
|
||||||
|
|
||||||
integrationTestCompile gebDependencies
|
integrationTestCompile gebDependencies
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample
|
package sample
|
||||||
|
|
||||||
import geb.spock.*
|
import geb.spock.*
|
||||||
@@ -12,19 +27,19 @@ import pages.*
|
|||||||
*/
|
*/
|
||||||
@Stepwise
|
@Stepwise
|
||||||
class AttributeTests extends GebReportingSpec {
|
class AttributeTests extends GebReportingSpec {
|
||||||
def 'first visit no attributes'() {
|
def 'first visit no attributes'() {
|
||||||
when:
|
when:
|
||||||
to HomePage
|
to HomePage
|
||||||
then:
|
then:
|
||||||
attributes.empty
|
attributes.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'create attribute'() {
|
def 'create attribute'() {
|
||||||
when:
|
when:
|
||||||
createAttribute('a','b')
|
createAttribute('a','b')
|
||||||
then:
|
then:
|
||||||
attributes.size() == 1
|
attributes.size() == 1
|
||||||
attributes[0].name == 'a'
|
attributes[0].name == 'a'
|
||||||
attributes[0].value == 'b'
|
attributes[0].value == 'b'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.pages
|
package sample.pages
|
||||||
|
|
||||||
import geb.*
|
import geb.*
|
||||||
@@ -8,23 +23,23 @@ import geb.*
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
class HomePage extends Page {
|
class HomePage extends Page {
|
||||||
static url = ''
|
static url = ''
|
||||||
static at = { assert driver.title == 'Session Attributes'; true}
|
static at = { assert driver.title == 'Session Attributes'; true}
|
||||||
static content = {
|
static content = {
|
||||||
form { $('form') }
|
form { $('form') }
|
||||||
submit { $('input[type=submit]') }
|
submit { $('input[type=submit]') }
|
||||||
createAttribute(required:false) { name, value ->
|
createAttribute(required:false) { name, value ->
|
||||||
form.attributeName = name
|
form.attributeName = name
|
||||||
form.attributeValue = value
|
form.attributeValue = value
|
||||||
submit.click(HomePage)
|
submit.click(HomePage)
|
||||||
}
|
}
|
||||||
attributes { moduleList AttributeRow, $("table tr").tail() }
|
attributes { moduleList AttributeRow, $("table tr").tail() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class AttributeRow extends Module {
|
class AttributeRow extends Module {
|
||||||
static content = {
|
static content = {
|
||||||
cell { $("td", it) }
|
cell { $("td", it) }
|
||||||
name { cell(0).text() }
|
name { cell(0).text() }
|
||||||
value { cell(1).text() }
|
value { cell(1).text() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
import org.springframework.session.data.redis.config.annotation.web.http.*;
|
import org.springframework.session.data.redis.config.annotation.web.http.*;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package sample;
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -14,14 +13,14 @@ package sample;
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
package sample;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
import redis.clients.jedis.Protocol;
|
import redis.clients.jedis.Protocol;
|
||||||
import redis.embedded.RedisServer;
|
import redis.embedded.RedisServer;
|
||||||
@@ -34,21 +33,21 @@ import redis.embedded.RedisServer;
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
public class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
public class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
||||||
private RedisServer redisServer;
|
private RedisServer redisServer;
|
||||||
|
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
||||||
redisServer.start();
|
redisServer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() throws Exception {
|
public void destroy() throws Exception {
|
||||||
if(redisServer != null) {
|
if(redisServer != null) {
|
||||||
redisServer.stop();
|
redisServer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
||||||
|
|
||||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
import javax.servlet.*;
|
import javax.servlet.*;
|
||||||
@@ -5,16 +20,19 @@ import javax.servlet.annotation.*;
|
|||||||
import javax.servlet.http.*;
|
import javax.servlet.http.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
// tag::class[]
|
||||||
@WebServlet("/session")
|
@WebServlet("/session")
|
||||||
public class SessionServlet extends HttpServlet {
|
public class SessionServlet extends HttpServlet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
String attributeName = req.getParameter("attributeName");
|
String attributeName = req.getParameter("attributeName");
|
||||||
String attributeValue = req.getParameter("attributeValue");
|
String attributeValue = req.getParameter("attributeValue");
|
||||||
req.getSession().setAttribute(attributeName, attributeValue);
|
req.getSession().setAttribute(attributeName, attributeValue);
|
||||||
resp.sendRedirect(req.getContextPath() + "/");
|
resp.sendRedirect(req.getContextPath() + "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final long serialVersionUID = 2878267318695777395L;
|
private static final long serialVersionUID = 2878267318695777395L;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// end::class[]
|
||||||
@@ -3,18 +3,18 @@ apply from: TOMCAT_GRADLE
|
|||||||
|
|
||||||
tasks.findByPath("artifactoryPublish")?.enabled = false
|
tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||||
sonarRunner {
|
sonarRunner {
|
||||||
skipProject = true
|
skipProject = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':spring-session-data-redis'),
|
compile project(':spring-session-data-redis'),
|
||||||
"org.springframework:spring-web:$springVersion",
|
"org.springframework:spring-web:$springVersion",
|
||||||
"redis.embedded:embedded-redis:0.2",
|
"redis.embedded:embedded-redis:0.2",
|
||||||
jstlDependencies
|
jstlDependencies
|
||||||
|
|
||||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||||
|
|
||||||
testCompile 'junit:junit:4.11'
|
testCompile 'junit:junit:4.11'
|
||||||
|
|
||||||
integrationTestCompile gebDependencies
|
integrationTestCompile gebDependencies
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample
|
package sample
|
||||||
|
|
||||||
import geb.spock.*
|
import geb.spock.*
|
||||||
@@ -12,19 +27,19 @@ import pages.*
|
|||||||
*/
|
*/
|
||||||
@Stepwise
|
@Stepwise
|
||||||
class AttributeTests extends GebReportingSpec {
|
class AttributeTests extends GebReportingSpec {
|
||||||
def 'first visit no attributes'() {
|
def 'first visit no attributes'() {
|
||||||
when:
|
when:
|
||||||
to HomePage
|
to HomePage
|
||||||
then:
|
then:
|
||||||
attributes.empty
|
attributes.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'create attribute'() {
|
def 'create attribute'() {
|
||||||
when:
|
when:
|
||||||
createAttribute('a','b')
|
createAttribute('a','b')
|
||||||
then:
|
then:
|
||||||
attributes.size() == 1
|
attributes.size() == 1
|
||||||
attributes[0].name == 'a'
|
attributes[0].name == 'a'
|
||||||
attributes[0].value == 'b'
|
attributes[0].value == 'b'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.pages
|
package sample.pages
|
||||||
|
|
||||||
import geb.*
|
import geb.*
|
||||||
@@ -8,23 +23,23 @@ import geb.*
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
class HomePage extends Page {
|
class HomePage extends Page {
|
||||||
static url = ''
|
static url = ''
|
||||||
static at = { assert driver.title == 'Session Attributes'; true}
|
static at = { assert driver.title == 'Session Attributes'; true}
|
||||||
static content = {
|
static content = {
|
||||||
form { $('form') }
|
form { $('form') }
|
||||||
submit { $('input[type=submit]') }
|
submit { $('input[type=submit]') }
|
||||||
createAttribute(required:false) { name, value ->
|
createAttribute(required:false) { name, value ->
|
||||||
form.attributeName = name
|
form.attributeName = name
|
||||||
form.attributeValue = value
|
form.attributeValue = value
|
||||||
submit.click(HomePage)
|
submit.click(HomePage)
|
||||||
}
|
}
|
||||||
attributes { moduleList AttributeRow, $("table tr").tail() }
|
attributes { moduleList AttributeRow, $("table tr").tail() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class AttributeRow extends Module {
|
class AttributeRow extends Module {
|
||||||
static content = {
|
static content = {
|
||||||
cell { $("td", it) }
|
cell { $("td", it) }
|
||||||
name { cell(0).text() }
|
name { cell(0).text() }
|
||||||
value { cell(1).text() }
|
value { cell(1).text() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.data.redis.connection.jedis.*;
|
import org.springframework.data.redis.connection.jedis.*;
|
||||||
import org.springframework.session.data.redis.config.annotation.web.http.*;
|
import org.springframework.session.data.redis.config.annotation.web.http.*;
|
||||||
|
|
||||||
|
// tag::class[]
|
||||||
@Import(EmbeddedRedisConfiguration.class) // <1>
|
@Import(EmbeddedRedisConfiguration.class) // <1>
|
||||||
@EnableRedisHttpSession // <2>
|
@EnableRedisHttpSession // <2>
|
||||||
public class Config {
|
public class Config {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public JedisConnectionFactory connectionFactory() {
|
public JedisConnectionFactory connectionFactory() {
|
||||||
return new JedisConnectionFactory(); // <3>
|
return new JedisConnectionFactory(); // <3>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// end::class[]
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package sample;
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -14,6 +13,8 @@ package sample;
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
package sample;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
@@ -36,34 +37,34 @@ import redis.embedded.RedisServer;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class EmbeddedRedisConfiguration {
|
public class EmbeddedRedisConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public static RedisServerBean redisServer() {
|
public static RedisServerBean redisServer() {
|
||||||
return new RedisServerBean();
|
return new RedisServerBean();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
||||||
* is initialized before any other Beans. Specifically, we want to ensure
|
* is initialized before any other Beans. Specifically, we want to ensure
|
||||||
* that the Redis Server is started before RedisHttpSessionConfiguration
|
* that the Redis Server is started before RedisHttpSessionConfiguration
|
||||||
* attempts to enable Keyspace notifications.
|
* attempts to enable Keyspace notifications.
|
||||||
*/
|
*/
|
||||||
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
||||||
private RedisServer redisServer;
|
private RedisServer redisServer;
|
||||||
|
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
||||||
redisServer.start();
|
redisServer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() throws Exception {
|
public void destroy() throws Exception {
|
||||||
if(redisServer != null) {
|
if(redisServer != null) {
|
||||||
redisServer.stop();
|
redisServer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
||||||
|
|
||||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
||||||
|
|
||||||
|
// tag::class[]
|
||||||
public class Initializer
|
public class Initializer
|
||||||
extends AbstractHttpSessionApplicationInitializer { // <1>
|
extends AbstractHttpSessionApplicationInitializer { // <1>
|
||||||
|
|
||||||
public Initializer() {
|
public Initializer() {
|
||||||
super(Config.class); // <2>
|
super(Config.class); // <2>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// end::class[]
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
import javax.servlet.*;
|
import javax.servlet.*;
|
||||||
@@ -5,16 +20,18 @@ import javax.servlet.annotation.*;
|
|||||||
import javax.servlet.http.*;
|
import javax.servlet.http.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
// tag::class[]
|
||||||
@WebServlet("/session")
|
@WebServlet("/session")
|
||||||
public class SessionServlet extends HttpServlet {
|
public class SessionServlet extends HttpServlet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
String attributeName = req.getParameter("attributeName");
|
String attributeName = req.getParameter("attributeName");
|
||||||
String attributeValue = req.getParameter("attributeValue");
|
String attributeValue = req.getParameter("attributeValue");
|
||||||
req.getSession().setAttribute(attributeName, attributeValue);
|
req.getSession().setAttribute(attributeName, attributeValue);
|
||||||
resp.sendRedirect(req.getContextPath() + "/");
|
resp.sendRedirect(req.getContextPath() + "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final long serialVersionUID = 2878267318695777395L;
|
private static final long serialVersionUID = 2878267318695777395L;
|
||||||
}
|
}
|
||||||
|
// tag::end[]
|
||||||
@@ -2,47 +2,47 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Session Attributes</title>
|
<title>Session Attributes</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap.min.css">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Description</h1>
|
<h1>Description</h1>
|
||||||
<p>This application demonstrates how to use a Redis instance to back your session. Notice that there is no JSESSIONID cookie. We are also able to customize the way of identifying what the requested session id is.</p>
|
<p>This application demonstrates how to use a Redis instance to back your session. Notice that there is no JSESSIONID cookie. We are also able to customize the way of identifying what the requested session id is.</p>
|
||||||
|
|
||||||
<h1>Try it</h1>
|
<h1>Try it</h1>
|
||||||
|
|
||||||
<form class="form-inline" role="form" action="./session" method="post">
|
<form class="form-inline" role="form" action="./session" method="post">
|
||||||
<label for="attributeValue">Attribute Name</label>
|
<label for="attributeValue">Attribute Name</label>
|
||||||
<input id="attributeValue" type="text" name="attributeName"/>
|
<input id="attributeValue" type="text" name="attributeName"/>
|
||||||
<label for="attributeValue">Attribute Value</label>
|
<label for="attributeValue">Attribute Value</label>
|
||||||
<input id="attributeValue" type="text" name="attributeValue"/>
|
<input id="attributeValue" type="text" name="attributeValue"/>
|
||||||
<input type="submit" value="Set Attribute"/>
|
<input type="submit" value="Set Attribute"/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Attribute Name</th>
|
<th>Attribute Name</th>
|
||||||
<th>Attribute Value</th>
|
<th>Attribute Value</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<c:forEach items="${sessionScope}" var="attr">
|
<c:forEach items="${sessionScope}" var="attr">
|
||||||
<tr>
|
<tr>
|
||||||
<td><c:out value="${attr.key}"/></td>
|
<td><c:out value="${attr.key}"/></td>
|
||||||
<td><c:out value="${attr.value}"/></td>
|
<td><c:out value="${attr.value}"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
</c:forEach>
|
</c:forEach>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -3,22 +3,22 @@ apply from: TOMCAT_GRADLE
|
|||||||
|
|
||||||
tasks.findByPath("artifactoryPublish")?.enabled = false
|
tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||||
sonarRunner {
|
sonarRunner {
|
||||||
skipProject = true
|
skipProject = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':spring-session-data-redis'),
|
compile project(':spring-session-data-redis'),
|
||||||
"org.springframework:spring-webmvc:$springVersion",
|
"org.springframework:spring-webmvc:$springVersion",
|
||||||
"redis.embedded:embedded-redis:0.2",
|
"redis.embedded:embedded-redis:0.2",
|
||||||
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
||||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||||
"com.fasterxml.jackson.core:jackson-databind:$jacksonVersion",
|
"com.fasterxml.jackson.core:jackson-databind:$jacksonVersion",
|
||||||
jstlDependencies
|
jstlDependencies
|
||||||
|
|
||||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||||
|
|
||||||
testCompile 'junit:junit:4.11'
|
testCompile 'junit:junit:4.11'
|
||||||
|
|
||||||
integrationTestCompile spockDependencies,
|
integrationTestCompile spockDependencies,
|
||||||
'org.codehaus.groovy.modules.http-builder:http-builder:0.7'
|
'org.codehaus.groovy.modules.http-builder:http-builder:0.7'
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample
|
package sample
|
||||||
|
|
||||||
import groovyx.net.http.HttpResponseException
|
import groovyx.net.http.HttpResponseException
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
package sample;
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
* you may not use this file except in compliance with the License.
|
||||||
* the License at
|
* 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
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* License for the specific language governing permissions and limitations under
|
* See the License for the specific language governing permissions and
|
||||||
* the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package sample;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
@@ -36,35 +36,33 @@ import redis.embedded.RedisServer;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class EmbeddedRedisConfiguration {
|
public class EmbeddedRedisConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public static RedisServerBean redisServer() {
|
public static RedisServerBean redisServer() {
|
||||||
return new RedisServerBean();
|
return new RedisServerBean();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
||||||
* is initialized before any other Beans. Specifically, we want to ensure
|
* is initialized before any other Beans. Specifically, we want to ensure
|
||||||
* that the Redis Server is started before RedisHttpSessionConfiguration
|
* that the Redis Server is started before RedisHttpSessionConfiguration
|
||||||
* attempts to enable Keyspace notifications.
|
* attempts to enable Keyspace notifications.
|
||||||
*/
|
*/
|
||||||
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
||||||
private RedisServer redisServer;
|
private RedisServer redisServer;
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
||||||
redisServer.start();
|
redisServer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() throws Exception {
|
public void destroy() throws Exception {
|
||||||
if(redisServer != null) {
|
if(redisServer != null) {
|
||||||
redisServer.stop();
|
redisServer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
||||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
|
||||||
|
|
||||||
@Override
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
||||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -8,18 +23,20 @@ import org.springframework.session.data.redis.config.annotation.web.http.EnableR
|
|||||||
import org.springframework.session.web.http.HeaderHttpSessionStrategy;
|
import org.springframework.session.web.http.HeaderHttpSessionStrategy;
|
||||||
import org.springframework.session.web.http.HttpSessionStrategy;
|
import org.springframework.session.web.http.HttpSessionStrategy;
|
||||||
|
|
||||||
|
// tag::class[]
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(EmbeddedRedisConfiguration.class) // <1>
|
@Import(EmbeddedRedisConfiguration.class) // <1>
|
||||||
@EnableRedisHttpSession // <2>
|
@EnableRedisHttpSession // <2>
|
||||||
public class HttpSessionConfig {
|
public class HttpSessionConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public JedisConnectionFactory connectionFactory() {
|
public JedisConnectionFactory connectionFactory() {
|
||||||
return new JedisConnectionFactory(); // <3>
|
return new JedisConnectionFactory(); // <3>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public HttpSessionStrategy httpSessionStrategy() {
|
public HttpSessionStrategy httpSessionStrategy() {
|
||||||
return new HeaderHttpSessionStrategy(); // <4>
|
return new HeaderHttpSessionStrategy(); // <4>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// end::class[]
|
||||||
@@ -1,7 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
||||||
|
|
||||||
|
// tag::class[]
|
||||||
public class Initializer extends AbstractHttpSessionApplicationInitializer {
|
public class Initializer extends AbstractHttpSessionApplicationInitializer {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// end::class[]
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -28,19 +28,19 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
|||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
.and()
|
.and()
|
||||||
.httpBasic();
|
.httpBasic();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
auth
|
auth
|
||||||
.inMemoryAuthentication()
|
.inMemoryAuthentication()
|
||||||
.withUser("user").password("password").roles("USER");
|
.withUser("user").password("password").roles("USER");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -21,6 +21,6 @@ import org.springframework.security.web.context.AbstractSecurityWebApplicationIn
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
public class SecurityInitializer extends
|
public class SecurityInitializer extends
|
||||||
AbstractSecurityWebApplicationInitializer {
|
AbstractSecurityWebApplicationInitializer {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -23,20 +23,20 @@ import sample.SecurityConfig;
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
|
public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
|
||||||
// tag::config[]
|
// tag::config[]
|
||||||
@Override
|
@Override
|
||||||
protected Class<?>[] getRootConfigClasses() {
|
protected Class<?>[] getRootConfigClasses() {
|
||||||
return new Class[] {SecurityConfig.class, HttpSessionConfig.class};
|
return new Class[] {SecurityConfig.class, HttpSessionConfig.class};
|
||||||
}
|
}
|
||||||
// end::config[]
|
// end::config[]
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?>[] getServletConfigClasses() {
|
protected Class<?>[] getServletConfigClasses() {
|
||||||
return new Class[] { MvcConfig.class };
|
return new Class[] { MvcConfig.class };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String[] getServletMappings() {
|
protected String[] getServletMappings() {
|
||||||
return new String[] { "/" };
|
return new String[] { "/" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -15,33 +15,33 @@
|
|||||||
*/
|
*/
|
||||||
package sample.mvc;
|
package sample.mvc;
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
public class RestDemoController {
|
public class RestDemoController {
|
||||||
|
|
||||||
@RequestMapping(value="/",produces = "application/json")
|
@RequestMapping(value="/",produces = "application/json")
|
||||||
public Map<String,String> helloUser(Principal principal) {
|
public Map<String,String> helloUser(Principal principal) {
|
||||||
HashMap<String,String> result = new HashMap<String,String>();
|
HashMap<String,String> result = new HashMap<String,String>();
|
||||||
result.put("username", principal.getName());
|
result.put("username", principal.getName());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/logout")
|
@RequestMapping("/logout")
|
||||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
public void logout(HttpSession session) {
|
public void logout(HttpSession session) {
|
||||||
session.invalidate();
|
session.invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,20 +3,20 @@ apply from: TOMCAT_GRADLE
|
|||||||
|
|
||||||
tasks.findByPath("artifactoryPublish")?.enabled = false
|
tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||||
sonarRunner {
|
sonarRunner {
|
||||||
skipProject = true
|
skipProject = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':spring-session-data-redis'),
|
compile project(':spring-session-data-redis'),
|
||||||
"org.springframework:spring-web:$springVersion",
|
"org.springframework:spring-web:$springVersion",
|
||||||
"redis.embedded:embedded-redis:0.2",
|
"redis.embedded:embedded-redis:0.2",
|
||||||
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
||||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||||
jstlDependencies
|
jstlDependencies
|
||||||
|
|
||||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||||
|
|
||||||
testCompile 'junit:junit:4.11'
|
testCompile 'junit:junit:4.11'
|
||||||
|
|
||||||
integrationTestCompile gebDependencies
|
integrationTestCompile gebDependencies
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample
|
package sample
|
||||||
|
|
||||||
import geb.spock.GebReportingSpec
|
import geb.spock.GebReportingSpec
|
||||||
@@ -14,37 +29,37 @@ import spock.lang.Stepwise
|
|||||||
@Stepwise
|
@Stepwise
|
||||||
class SecurityTests extends GebReportingSpec {
|
class SecurityTests extends GebReportingSpec {
|
||||||
|
|
||||||
def 'Unauthenticated user sent to log in page'() {
|
def 'Unauthenticated user sent to log in page'() {
|
||||||
when: 'unauthenticated user request protected page'
|
when: 'unauthenticated user request protected page'
|
||||||
via HomePage
|
via HomePage
|
||||||
then: 'sent to the log in page'
|
then: 'sent to the log in page'
|
||||||
at LoginPage
|
at LoginPage
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'Log in views home page'() {
|
def 'Log in views home page'() {
|
||||||
when: 'log in successfully'
|
when: 'log in successfully'
|
||||||
login()
|
login()
|
||||||
then: 'sent to original page'
|
then: 'sent to original page'
|
||||||
at HomePage
|
at HomePage
|
||||||
and: 'the username is displayed'
|
and: 'the username is displayed'
|
||||||
username == 'user'
|
username == 'user'
|
||||||
and: 'Spring Session Management is being used'
|
and: 'Spring Session Management is being used'
|
||||||
driver.manage().cookies.find { it.name == 'SESSION' }
|
driver.manage().cookies.find { it.name == 'SESSION' }
|
||||||
and: 'Standard Session is NOT being used'
|
and: 'Standard Session is NOT being used'
|
||||||
!driver.manage().cookies.find { it.name == 'JSESSIONID' }
|
!driver.manage().cookies.find { it.name == 'JSESSIONID' }
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'Log out success'() {
|
def 'Log out success'() {
|
||||||
when:
|
when:
|
||||||
logout()
|
logout()
|
||||||
then:
|
then:
|
||||||
at LoginPage
|
at LoginPage
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'Logged out user sent to log in page'() {
|
def 'Logged out user sent to log in page'() {
|
||||||
when: 'logged out user request protected page'
|
when: 'logged out user request protected page'
|
||||||
via HomePage
|
via HomePage
|
||||||
then: 'sent to the log in page'
|
then: 'sent to the log in page'
|
||||||
at LoginPage
|
at LoginPage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.pages
|
package sample.pages
|
||||||
|
|
||||||
import geb.Page
|
import geb.Page
|
||||||
@@ -8,10 +23,10 @@ import geb.Page
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
class HomePage extends Page {
|
class HomePage extends Page {
|
||||||
static url = ''
|
static url = ''
|
||||||
static at = { assert driver.title == 'Secured Content'; true}
|
static at = { assert driver.title == 'Secured Content'; true}
|
||||||
static content = {
|
static content = {
|
||||||
username { $('#un').text() }
|
username { $('#un').text() }
|
||||||
logout(to:LoginPage) { $('input[type=submit]').click() }
|
logout(to:LoginPage) { $('input[type=submit]').click() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.pages
|
package sample.pages
|
||||||
|
|
||||||
import geb.Page
|
import geb.Page
|
||||||
@@ -8,15 +23,15 @@ import geb.Page
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
class LoginPage extends Page {
|
class LoginPage extends Page {
|
||||||
static url = '/login'
|
static url = '/login'
|
||||||
static at = { assert driver.title == 'Login Page'; true}
|
static at = { assert driver.title == 'Login Page'; true}
|
||||||
static content = {
|
static content = {
|
||||||
form { $('form') }
|
form { $('form') }
|
||||||
submit { $('input[type=submit]') }
|
submit { $('input[type=submit]') }
|
||||||
login(required:false) { user='user', pass='password' ->
|
login(required:false) { user='user', pass='password' ->
|
||||||
form.username = user
|
form.username = user
|
||||||
form.password = pass
|
form.password = pass
|
||||||
submit.click(HomePage)
|
submit.click(HomePage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -6,13 +21,15 @@ import org.springframework.context.annotation.Import;
|
|||||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||||
|
|
||||||
|
// tag::class[]
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(EmbeddedRedisConfiguration.class) // <1>
|
@Import(EmbeddedRedisConfiguration.class) // <1>
|
||||||
@EnableRedisHttpSession // <2>
|
@EnableRedisHttpSession // <2>
|
||||||
public class Config {
|
public class Config {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public JedisConnectionFactory connectionFactory() {
|
public JedisConnectionFactory connectionFactory() {
|
||||||
return new JedisConnectionFactory(); // <3>
|
return new JedisConnectionFactory(); // <3>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// end::class[]
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package sample;
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -14,6 +13,8 @@ package sample;
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
package sample;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
@@ -36,35 +37,33 @@ import redis.embedded.RedisServer;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class EmbeddedRedisConfiguration {
|
public class EmbeddedRedisConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public static RedisServerBean redisServer() {
|
public static RedisServerBean redisServer() {
|
||||||
return new RedisServerBean();
|
return new RedisServerBean();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
||||||
* is initialized before any other Beans. Specifically, we want to ensure
|
* is initialized before any other Beans. Specifically, we want to ensure
|
||||||
* that the Redis Server is started before RedisHttpSessionConfiguration
|
* that the Redis Server is started before RedisHttpSessionConfiguration
|
||||||
* attempts to enable Keyspace notifications.
|
* attempts to enable Keyspace notifications.
|
||||||
*/
|
*/
|
||||||
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
||||||
private RedisServer redisServer;
|
private RedisServer redisServer;
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
||||||
redisServer.start();
|
redisServer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() throws Exception {
|
public void destroy() throws Exception {
|
||||||
if(redisServer != null) {
|
if(redisServer != null) {
|
||||||
redisServer.stop();
|
redisServer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
||||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
|
||||||
|
|
||||||
@Override
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
||||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
||||||
|
|
||||||
|
// tag::class[]
|
||||||
public class Initializer extends AbstractHttpSessionApplicationInitializer {
|
public class Initializer extends AbstractHttpSessionApplicationInitializer {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// end::class[]
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -25,10 +25,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
|||||||
|
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
@Autowired
|
@Autowired
|
||||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
auth
|
auth
|
||||||
.inMemoryAuthentication()
|
.inMemoryAuthentication()
|
||||||
.withUser("user").password("password").roles("USER");
|
.withUser("user").password("password").roles("USER");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
|
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
|
||||||
|
|
||||||
|
// tag::class[]
|
||||||
public class SecurityInitializer extends
|
public class SecurityInitializer extends
|
||||||
AbstractSecurityWebApplicationInitializer {
|
AbstractSecurityWebApplicationInitializer {
|
||||||
|
|
||||||
public SecurityInitializer() {
|
public SecurityInitializer() {
|
||||||
super(SecurityConfig.class, Config.class);
|
super(SecurityConfig.class, Config.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// end::class[]
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
@@ -10,13 +25,13 @@ import java.io.IOException;
|
|||||||
@WebServlet("/session")
|
@WebServlet("/session")
|
||||||
public class SessionServlet extends HttpServlet {
|
public class SessionServlet extends HttpServlet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
String attributeName = req.getParameter("attributeName");
|
String attributeName = req.getParameter("attributeName");
|
||||||
String attributeValue = req.getParameter("attributeValue");
|
String attributeValue = req.getParameter("attributeValue");
|
||||||
req.getSession().setAttribute(attributeName, attributeValue);
|
req.getSession().setAttribute(attributeName, attributeValue);
|
||||||
resp.sendRedirect(req.getContextPath() + "/");
|
resp.sendRedirect(req.getContextPath() + "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final long serialVersionUID = 2878267318695777395L;
|
private static final long serialVersionUID = 2878267318695777395L;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,31 +2,31 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Secured Content</title>
|
<title>Secured Content</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap.min.css">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
#un {
|
#un {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Description</h1>
|
<h1>Description</h1>
|
||||||
<p>This demonstrates how Spring Session can be combined with Spring Security. The important thing to ensure is that Spring Session's Filter is included before Spring Security's Filter.</p>
|
<p>This demonstrates how Spring Session can be combined with Spring Security. The important thing to ensure is that Spring Session's Filter is included before Spring Security's Filter.</p>
|
||||||
|
|
||||||
<h1>Logged in as</h1>
|
<h1>Logged in as</h1>
|
||||||
|
|
||||||
<p>You are currently logged in as <span id="un"><c:out value="${pageContext.request.remoteUser}"/></span>.</p>
|
<p>You are currently logged in as <span id="un"><c:out value="${pageContext.request.remoteUser}"/></span>.</p>
|
||||||
|
|
||||||
<c:url value="/logout" var="logoutUrl"/>
|
<c:url value="/logout" var="logoutUrl"/>
|
||||||
<form action="${logoutUrl}" method="post">
|
<form action="${logoutUrl}" method="post">
|
||||||
<input type="submit" value="Log Out"/>
|
<input type="submit" value="Log Out"/>
|
||||||
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
|
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -3,18 +3,18 @@ apply from: TOMCAT_GRADLE
|
|||||||
|
|
||||||
tasks.findByPath("artifactoryPublish")?.enabled = false
|
tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||||
sonarRunner {
|
sonarRunner {
|
||||||
skipProject = true
|
skipProject = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':spring-session-data-redis'),
|
compile project(':spring-session-data-redis'),
|
||||||
"org.springframework:spring-web:$springVersion",
|
"org.springframework:spring-web:$springVersion",
|
||||||
"redis.embedded:embedded-redis:0.2",
|
"redis.embedded:embedded-redis:0.2",
|
||||||
jstlDependencies
|
jstlDependencies
|
||||||
|
|
||||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||||
|
|
||||||
testCompile 'junit:junit:4.11'
|
testCompile 'junit:junit:4.11'
|
||||||
|
|
||||||
integrationTestCompile gebDependencies
|
integrationTestCompile gebDependencies
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample
|
package sample
|
||||||
|
|
||||||
import geb.spock.*
|
import geb.spock.*
|
||||||
@@ -13,112 +28,112 @@ import pages.*
|
|||||||
*/
|
*/
|
||||||
@Stepwise
|
@Stepwise
|
||||||
class UserTests extends GebReportingSpec {
|
class UserTests extends GebReportingSpec {
|
||||||
def 'first visit not authenticated'() {
|
def 'first visit not authenticated'() {
|
||||||
when:
|
when:
|
||||||
to HomePage
|
to HomePage
|
||||||
then:
|
then:
|
||||||
form
|
form
|
||||||
!username
|
!username
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'invalid login'() {
|
def 'invalid login'() {
|
||||||
setup:
|
setup:
|
||||||
def user = 'rob'
|
def user = 'rob'
|
||||||
when:
|
when:
|
||||||
login(user, user+'invalid')
|
login(user, user+'invalid')
|
||||||
then:
|
then:
|
||||||
!username
|
!username
|
||||||
error == 'Invalid username / password. Please ensure the username is the same as the password.'
|
error == 'Invalid username / password. Please ensure the username is the same as the password.'
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'empty username'() {
|
def 'empty username'() {
|
||||||
setup:
|
setup:
|
||||||
def user = ''
|
def user = ''
|
||||||
when:
|
when:
|
||||||
login(user, user)
|
login(user, user)
|
||||||
then:
|
then:
|
||||||
!username
|
!username
|
||||||
error == 'Invalid username / password. Please ensure the username is the same as the password.'
|
error == 'Invalid username / password. Please ensure the username is the same as the password.'
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'login single user'() {
|
def 'login single user'() {
|
||||||
setup:
|
setup:
|
||||||
def user = 'rob'
|
def user = 'rob'
|
||||||
when:
|
when:
|
||||||
login(user, user)
|
login(user, user)
|
||||||
then:
|
then:
|
||||||
username == user
|
username == user
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'add account'() {
|
def 'add account'() {
|
||||||
when:
|
when:
|
||||||
addAccount.click(HomePage)
|
addAccount.click(HomePage)
|
||||||
then:
|
then:
|
||||||
form
|
form
|
||||||
!username
|
!username
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'log in second user'() {
|
def 'log in second user'() {
|
||||||
setup:
|
setup:
|
||||||
def user = 'luke'
|
def user = 'luke'
|
||||||
when:
|
when:
|
||||||
login(user, user)
|
login(user, user)
|
||||||
then:
|
then:
|
||||||
username == user
|
username == user
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'following links keeps new session'() {
|
def 'following links keeps new session'() {
|
||||||
when:
|
when:
|
||||||
navLink.click(LinkPage)
|
navLink.click(LinkPage)
|
||||||
then:
|
then:
|
||||||
username == 'luke'
|
username == 'luke'
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'switch account rob'() {
|
def 'switch account rob'() {
|
||||||
setup:
|
setup:
|
||||||
def user = 'rob'
|
def user = 'rob'
|
||||||
when:
|
when:
|
||||||
switchAccount(user)
|
switchAccount(user)
|
||||||
then:
|
then:
|
||||||
username == user
|
username == user
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'following links keeps original session'() {
|
def 'following links keeps original session'() {
|
||||||
when:
|
when:
|
||||||
navLink.click(LinkPage)
|
navLink.click(LinkPage)
|
||||||
then:
|
then:
|
||||||
username == 'rob'
|
username == 'rob'
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'switch account luke'() {
|
def 'switch account luke'() {
|
||||||
setup:
|
setup:
|
||||||
def user = 'luke'
|
def user = 'luke'
|
||||||
when:
|
when:
|
||||||
switchAccount(user)
|
switchAccount(user)
|
||||||
then:
|
then:
|
||||||
username == user
|
username == user
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'logout luke'() {
|
def 'logout luke'() {
|
||||||
when:
|
when:
|
||||||
logout.click(HomePage)
|
logout.click(HomePage)
|
||||||
then:
|
then:
|
||||||
!username
|
!username
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'switch back rob'() {
|
def 'switch back rob'() {
|
||||||
setup:
|
setup:
|
||||||
def user = 'rob'
|
def user = 'rob'
|
||||||
when:
|
when:
|
||||||
switchAccount(user)
|
switchAccount(user)
|
||||||
then:
|
then:
|
||||||
username == user
|
username == user
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'logout rob'() {
|
def 'logout rob'() {
|
||||||
when:
|
when:
|
||||||
logout.click(HomePage)
|
logout.click(HomePage)
|
||||||
then:
|
then:
|
||||||
!username
|
!username
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.pages
|
package sample.pages
|
||||||
|
|
||||||
import geb.*
|
import geb.*
|
||||||
@@ -8,24 +23,24 @@ import geb.*
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
class HomePage extends Page {
|
class HomePage extends Page {
|
||||||
static url = ''
|
static url = ''
|
||||||
static at = { assert driver.title == 'Demonstrates Multi User Log In'; true}
|
static at = { assert driver.title == 'Demonstrates Multi User Log In'; true}
|
||||||
static content = {
|
static content = {
|
||||||
navLink { $('#navLink') }
|
navLink { $('#navLink') }
|
||||||
error { $('#error').text() }
|
error { $('#error').text() }
|
||||||
form { $('form') }
|
form { $('form') }
|
||||||
username(required:false) { $('#un').text() }
|
username(required:false) { $('#un').text() }
|
||||||
logout(required:false) { $('#logout') }
|
logout(required:false) { $('#logout') }
|
||||||
addAccount(required:false) { $('#addAccount') }
|
addAccount(required:false) { $('#addAccount') }
|
||||||
submit { $('input[type=submit]') }
|
submit { $('input[type=submit]') }
|
||||||
login(required:false) { user, pass ->
|
login(required:false) { user, pass ->
|
||||||
form.username = user
|
form.username = user
|
||||||
form.password = pass
|
form.password = pass
|
||||||
submit.click(HomePage)
|
submit.click(HomePage)
|
||||||
}
|
}
|
||||||
switchAccount{ un ->
|
switchAccount{ un ->
|
||||||
$("#switchAccount${un}").click(HomePage)
|
$("#switchAccount${un}").click(HomePage)
|
||||||
}
|
}
|
||||||
attributes { moduleList AttributeRow, $("table tr").tail() }
|
attributes { moduleList AttributeRow, $("table tr").tail() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.pages
|
package sample.pages
|
||||||
|
|
||||||
import geb.*
|
import geb.*
|
||||||
@@ -8,13 +23,13 @@ import geb.*
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
class LinkPage extends Page {
|
class LinkPage extends Page {
|
||||||
static url = ''
|
static url = ''
|
||||||
static at = { assert driver.title == 'Linked Page'; true}
|
static at = { assert driver.title == 'Linked Page'; true}
|
||||||
static content = {
|
static content = {
|
||||||
form { $('#navLinks') }
|
form { $('#navLinks') }
|
||||||
username(required:false) { $('#un').text() }
|
username(required:false) { $('#un').text() }
|
||||||
switchAccount{ un ->
|
switchAccount{ un ->
|
||||||
$("#switchAccount${un}").click(HomePage)
|
$("#switchAccount${un}").click(HomePage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,29 +16,29 @@
|
|||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
public class Account {
|
public class Account {
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
private String logoutUrl;
|
private String logoutUrl;
|
||||||
|
|
||||||
private String switchAccountUrl;
|
private String switchAccountUrl;
|
||||||
|
|
||||||
public Account(String username, String logoutUrl, String switchAccountUrl) {
|
public Account(String username, String logoutUrl, String switchAccountUrl) {
|
||||||
super();
|
super();
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.logoutUrl = logoutUrl;
|
this.logoutUrl = logoutUrl;
|
||||||
this.switchAccountUrl = switchAccountUrl;
|
this.switchAccountUrl = switchAccountUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLogoutUrl() {
|
public String getLogoutUrl() {
|
||||||
return logoutUrl;
|
return logoutUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSwitchAccountUrl() {
|
public String getSwitchAccountUrl() {
|
||||||
return switchAccountUrl;
|
return switchAccountUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package sample;
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -14,6 +13,7 @@ package sample;
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
package sample;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -24,13 +24,15 @@ import org.springframework.session.data.redis.config.annotation.web.http.EnableR
|
|||||||
/**
|
/**
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
|
// tag::class[]
|
||||||
@Import(EmbeddedRedisConfiguration.class)
|
@Import(EmbeddedRedisConfiguration.class)
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableRedisHttpSession
|
@EnableRedisHttpSession
|
||||||
public class Config {
|
public class Config {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public JedisConnectionFactory connectionFactory() {
|
public JedisConnectionFactory connectionFactory() {
|
||||||
return new JedisConnectionFactory();
|
return new JedisConnectionFactory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// end::class[]
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package sample;
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -14,6 +13,8 @@ package sample;
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
package sample;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
@@ -36,36 +37,34 @@ import redis.embedded.RedisServer;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class EmbeddedRedisConfiguration {
|
public class EmbeddedRedisConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public RedisServerBean redisServer() {
|
public RedisServerBean redisServer() {
|
||||||
return new RedisServerBean();
|
return new RedisServerBean();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
||||||
* is initialized before any other Beans. Specifically, we want to ensure
|
* is initialized before any other Beans. Specifically, we want to ensure
|
||||||
* that the Redis Server is started before RedisHttpSessionConfiguration
|
* that the Redis Server is started before RedisHttpSessionConfiguration
|
||||||
* attempts to enable Keyspace notifications.
|
* attempts to enable Keyspace notifications.
|
||||||
*/
|
*/
|
||||||
class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
||||||
private RedisServer redisServer;
|
private RedisServer redisServer;
|
||||||
|
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
||||||
redisServer.start();
|
redisServer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() throws Exception {
|
public void destroy() throws Exception {
|
||||||
if(redisServer != null) {
|
if(redisServer != null) {
|
||||||
redisServer.stop();
|
redisServer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
||||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
|
||||||
|
|
||||||
@Override
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
||||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package sample;
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -14,7 +13,7 @@ package sample;
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
package sample;
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
@@ -25,12 +24,12 @@ import org.springframework.session.web.context.AbstractHttpSessionApplicationIni
|
|||||||
*/
|
*/
|
||||||
public class Initializer extends AbstractHttpSessionApplicationInitializer {
|
public class Initializer extends AbstractHttpSessionApplicationInitializer {
|
||||||
|
|
||||||
public Initializer() {
|
public Initializer() {
|
||||||
super(Config.class);
|
super(Config.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void afterSessionRepositoryFilter(ServletContext servletContext) {
|
protected void afterSessionRepositoryFilter(ServletContext servletContext) {
|
||||||
appendFilters(servletContext, new UserAccountsFilter());
|
appendFilters(servletContext, new UserAccountsFilter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -26,21 +26,21 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
@WebServlet("/login")
|
@WebServlet("/login")
|
||||||
public class LoginServlet extends HttpServlet {
|
public class LoginServlet extends HttpServlet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
String username = req.getParameter("username");
|
String username = req.getParameter("username");
|
||||||
String password = req.getParameter("password");
|
String password = req.getParameter("password");
|
||||||
|
|
||||||
if(username != null && !"".equals(username) && username.equals(password)) {
|
if(username != null && !"".equals(username) && username.equals(password)) {
|
||||||
req.getSession().setAttribute("username", username);
|
req.getSession().setAttribute("username", username);
|
||||||
String url = resp.encodeRedirectURL(req.getContextPath() + "/");
|
String url = resp.encodeRedirectURL(req.getContextPath() + "/");
|
||||||
resp.sendRedirect(url);
|
resp.sendRedirect(url);
|
||||||
} else {
|
} else {
|
||||||
String url = resp.encodeRedirectURL(req.getContextPath() + "/?error");
|
String url = resp.encodeRedirectURL(req.getContextPath() + "/?error");
|
||||||
resp.sendRedirect(url);
|
resp.sendRedirect(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final long serialVersionUID = -8157634860354132501L;
|
private static final long serialVersionUID = -8157634860354132501L;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -27,16 +27,16 @@ import javax.servlet.http.HttpSession;
|
|||||||
@WebServlet("/logout")
|
@WebServlet("/logout")
|
||||||
public class LogoutServlet extends HttpServlet {
|
public class LogoutServlet extends HttpServlet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
|
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
HttpSession session = req.getSession(false);
|
HttpSession session = req.getSession(false);
|
||||||
if(session != null) {
|
if(session != null) {
|
||||||
session.invalidate();
|
session.invalidate();
|
||||||
}
|
}
|
||||||
String url = resp.encodeRedirectURL(req.getContextPath() + "/");
|
String url = resp.encodeRedirectURL(req.getContextPath() + "/");
|
||||||
resp.sendRedirect(url);
|
resp.sendRedirect(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final long serialVersionUID = 4061762524521437433L;
|
private static final long serialVersionUID = 4061762524521437433L;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -34,68 +34,68 @@ import org.springframework.session.web.http.HttpSessionManager;
|
|||||||
|
|
||||||
public class UserAccountsFilter implements Filter {
|
public class UserAccountsFilter implements Filter {
|
||||||
|
|
||||||
public void init(FilterConfig filterConfig) throws ServletException {
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void doFilter(ServletRequest request, ServletResponse response,
|
public void doFilter(ServletRequest request, ServletResponse response,
|
||||||
FilterChain chain) throws IOException, ServletException {
|
FilterChain chain) throws IOException, ServletException {
|
||||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||||
|
|
||||||
// tag::HttpSessionManager[]
|
// tag::HttpSessionManager[]
|
||||||
HttpSessionManager sessionManager =
|
HttpSessionManager sessionManager =
|
||||||
(HttpSessionManager) httpRequest.getAttribute(HttpSessionManager.class.getName());
|
(HttpSessionManager) httpRequest.getAttribute(HttpSessionManager.class.getName());
|
||||||
// end::HttpSessionManager[]
|
// end::HttpSessionManager[]
|
||||||
SessionRepository<Session> repo =
|
SessionRepository<Session> repo =
|
||||||
(SessionRepository<Session>) httpRequest.getAttribute(SessionRepository.class.getName());
|
(SessionRepository<Session>) httpRequest.getAttribute(SessionRepository.class.getName());
|
||||||
|
|
||||||
String currentSessionAlias = sessionManager.getCurrentSessionAlias(httpRequest);
|
String currentSessionAlias = sessionManager.getCurrentSessionAlias(httpRequest);
|
||||||
Map<String, String> sessionIds = sessionManager.getSessionIds(httpRequest);
|
Map<String, String> sessionIds = sessionManager.getSessionIds(httpRequest);
|
||||||
String unauthenticatedAlias = null;
|
String unauthenticatedAlias = null;
|
||||||
|
|
||||||
String contextPath = httpRequest.getContextPath();
|
String contextPath = httpRequest.getContextPath();
|
||||||
List<Account> accounts = new ArrayList<Account>();
|
List<Account> accounts = new ArrayList<Account>();
|
||||||
Account currentAccount = null;
|
Account currentAccount = null;
|
||||||
for(Map.Entry<String, String> entry : sessionIds.entrySet()) {
|
for(Map.Entry<String, String> entry : sessionIds.entrySet()) {
|
||||||
String alias = entry.getKey();
|
String alias = entry.getKey();
|
||||||
String sessionId = entry.getValue();
|
String sessionId = entry.getValue();
|
||||||
|
|
||||||
Session session = repo.getSession(sessionId);
|
Session session = repo.getSession(sessionId);
|
||||||
if(session == null) {
|
if(session == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String username = session.getAttribute("username");
|
String username = session.getAttribute("username");
|
||||||
if(username == null) {
|
if(username == null) {
|
||||||
unauthenticatedAlias = alias;
|
unauthenticatedAlias = alias;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String logoutUrl = sessionManager.encodeURL("./logout", alias);
|
String logoutUrl = sessionManager.encodeURL("./logout", alias);
|
||||||
String switchAccountUrl = sessionManager.encodeURL("./", alias);
|
String switchAccountUrl = sessionManager.encodeURL("./", alias);
|
||||||
Account account = new Account(username, logoutUrl, switchAccountUrl);
|
Account account = new Account(username, logoutUrl, switchAccountUrl);
|
||||||
if(currentSessionAlias.equals(alias)) {
|
if(currentSessionAlias.equals(alias)) {
|
||||||
currentAccount = account;
|
currentAccount = account;
|
||||||
} else {
|
} else {
|
||||||
accounts.add(account);
|
accounts.add(account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tag::addAccountUrl[]
|
// tag::addAccountUrl[]
|
||||||
String addAlias = unauthenticatedAlias == null ? // <1>
|
String addAlias = unauthenticatedAlias == null ? // <1>
|
||||||
sessionManager.getNewSessionAlias(httpRequest) : // <2>
|
sessionManager.getNewSessionAlias(httpRequest) : // <2>
|
||||||
unauthenticatedAlias; // <3>
|
unauthenticatedAlias; // <3>
|
||||||
String addAccountUrl = sessionManager.encodeURL(contextPath, addAlias); // <4>
|
String addAccountUrl = sessionManager.encodeURL(contextPath, addAlias); // <4>
|
||||||
// end::addAccountUrl[]
|
// end::addAccountUrl[]
|
||||||
|
|
||||||
httpRequest.setAttribute("currentAccount", currentAccount);
|
httpRequest.setAttribute("currentAccount", currentAccount);
|
||||||
httpRequest.setAttribute("addAccountUrl", addAccountUrl);
|
httpRequest.setAttribute("addAccountUrl", addAccountUrl);
|
||||||
httpRequest.setAttribute("accounts", accounts);
|
httpRequest.setAttribute("accounts", accounts);
|
||||||
|
|
||||||
chain.doFilter(request, response);
|
chain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
|
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'spring-boot'
|
apply plugin: 'spring-boot'
|
||||||
@@ -17,23 +17,23 @@ tasks.findByPath("artifactoryPublish")?.enabled = false
|
|||||||
group = 'samples'
|
group = 'samples'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':spring-session-data-redis'),
|
compile project(':spring-session-data-redis'),
|
||||||
"org.springframework.boot:spring-boot-starter-web",
|
"org.springframework.boot:spring-boot-starter-web",
|
||||||
"org.springframework.boot:spring-boot-starter-data-jpa",
|
"org.springframework.boot:spring-boot-starter-data-jpa",
|
||||||
"org.springframework.boot:spring-boot-starter-thymeleaf",
|
"org.springframework.boot:spring-boot-starter-thymeleaf",
|
||||||
"org.springframework.boot:spring-boot-starter-websocket",
|
"org.springframework.boot:spring-boot-starter-websocket",
|
||||||
"org.springframework:spring-websocket:${springVersion}",
|
"org.springframework:spring-websocket:${springVersion}",
|
||||||
"org.springframework.data:spring-data-jpa:1.7.0.RELEASE",
|
"org.springframework.data:spring-data-jpa:1.7.0.RELEASE",
|
||||||
"redis.embedded:embedded-redis:0.2",
|
"redis.embedded:embedded-redis:0.2",
|
||||||
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
|
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
|
||||||
"com.h2database:h2",
|
"com.h2database:h2",
|
||||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||||
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
||||||
"org.springframework.security:spring-security-messaging:$springSecurityVersion",
|
"org.springframework.security:spring-security-messaging:$springSecurityVersion",
|
||||||
"org.springframework.security:spring-security-data:$springSecurityVersion"
|
"org.springframework.security:spring-security-data:$springSecurityVersion"
|
||||||
|
|
||||||
|
|
||||||
testCompile "org.springframework.boot:spring-boot-starter-test",
|
testCompile "org.springframework.boot:spring-boot-starter-test",
|
||||||
"org.springframework.security:spring-security-test:$springSecurityVersion"
|
"org.springframework.security:spring-security-test:$springSecurityVersion"
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -28,7 +28,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
public class Application {
|
public class Application {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(Application.class, args);
|
SpringApplication.run(Application.class, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.config;
|
package sample.config;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
@@ -11,14 +26,14 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class DataSourceConfig {
|
public class DataSourceConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public DataSource dataSource() {
|
public DataSource dataSource() {
|
||||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
|
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
|
||||||
return builder.setType(EmbeddedDatabaseType.H2).build();
|
return builder.setType(EmbeddedDatabaseType.H2).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public JedisConnectionFactory connectionFactory() throws Exception {
|
public JedisConnectionFactory connectionFactory() throws Exception {
|
||||||
return new JedisConnectionFactory();
|
return new JedisConnectionFactory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -33,36 +33,34 @@ import redis.embedded.RedisServer;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class EmbeddedRedisConfig {
|
public class EmbeddedRedisConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public static RedisServerBean redisServer() {
|
public static RedisServerBean redisServer() {
|
||||||
return new RedisServerBean();
|
return new RedisServerBean();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
|
||||||
* is initialized before any other Beans. Specifically, we want to ensure
|
* is initialized before any other Beans. Specifically, we want to ensure
|
||||||
* that the Redis Server is started before RedisHttpSessionConfiguration
|
* that the Redis Server is started before RedisHttpSessionConfiguration
|
||||||
* attempts to enable Keyspace notifications.
|
* attempts to enable Keyspace notifications.
|
||||||
*/
|
*/
|
||||||
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
|
||||||
private RedisServer redisServer;
|
private RedisServer redisServer;
|
||||||
|
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
||||||
redisServer.start();
|
redisServer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() throws Exception {
|
public void destroy() throws Exception {
|
||||||
if(redisServer != null) {
|
if(redisServer != null) {
|
||||||
redisServer.stop();
|
redisServer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
||||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
|
|
||||||
|
|
||||||
@Override
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
||||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -32,7 +32,7 @@ public class H2Initializer {
|
|||||||
@Bean
|
@Bean
|
||||||
public ServletRegistrationBean h2Servlet() {
|
public ServletRegistrationBean h2Servlet() {
|
||||||
ServletRegistrationBean servletBean = new ServletRegistrationBean();
|
ServletRegistrationBean servletBean = new ServletRegistrationBean();
|
||||||
servletBean.addUrlMappings("/h2/*");
|
servletBean.addUrlMappings("/h2/*");
|
||||||
servletBean.setServlet(new WebServlet());
|
servletBean.setServlet(new WebServlet());
|
||||||
return servletBean;
|
return servletBean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -34,31 +34,34 @@ import org.springframework.session.data.redis.config.annotation.web.http.EnableR
|
|||||||
// tag::enable-redis-httpsession[]
|
// tag::enable-redis-httpsession[]
|
||||||
@EnableRedisHttpSession//(maxInactiveIntervalInSeconds = 60)
|
@EnableRedisHttpSession//(maxInactiveIntervalInSeconds = 60)
|
||||||
public class WebSecurityConfig
|
public class WebSecurityConfig
|
||||||
extends WebSecurityConfigurerAdapter {
|
extends WebSecurityConfigurerAdapter {
|
||||||
// end::enable-redis-httpsession[]
|
// end::enable-redis-httpsession[]
|
||||||
|
|
||||||
@Override
|
// @formatter:off
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http
|
||||||
|
.authorizeRequests()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
.and()
|
||||||
|
.formLogin()
|
||||||
|
.and()
|
||||||
|
.logout()
|
||||||
|
.permitAll();
|
||||||
|
}
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
http
|
// @formatter:off
|
||||||
.authorizeRequests()
|
@Autowired
|
||||||
.anyRequest().authenticated()
|
public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsService userDetailsService) throws Exception {
|
||||||
.and()
|
auth
|
||||||
.formLogin()
|
.userDetailsService(userDetailsService)
|
||||||
.and()
|
.passwordEncoder(new BCryptPasswordEncoder());
|
||||||
.logout()
|
}
|
||||||
.permitAll();
|
// @formatter:on
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired
|
@Bean
|
||||||
public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsService userDetailsService) throws Exception {
|
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
|
||||||
auth
|
return new SecurityEvaluationContextExtension();
|
||||||
.userDetailsService(userDetailsService)
|
}
|
||||||
.passwordEncoder(new BCryptPasswordEncoder());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
|
|
||||||
return new SecurityEvaluationContextExtension();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package sample.config;
|
package sample.config;
|
||||||
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
@@ -29,16 +28,16 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
|||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@EnableWebSocketMessageBroker
|
@EnableWebSocketMessageBroker
|
||||||
public class WebSocketConfig
|
public class WebSocketConfig
|
||||||
extends AbstractSessionWebSocketMessageBrokerConfigurer<ExpiringSession> { // <1>
|
extends AbstractSessionWebSocketMessageBrokerConfigurer<ExpiringSession> { // <1>
|
||||||
|
|
||||||
protected void configureStompEndpoints(StompEndpointRegistry registry) { // <2>
|
protected void configureStompEndpoints(StompEndpointRegistry registry) { // <2>
|
||||||
registry.addEndpoint("/messages")
|
registry.addEndpoint("/messages")
|
||||||
.withSockJS();
|
.withSockJS();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||||
registry.enableSimpleBroker("/queue/", "/topic/");
|
registry.enableSimpleBroker("/queue/", "/topic/");
|
||||||
registry.setApplicationDestinationPrefixes("/app");
|
registry.setApplicationDestinationPrefixes("/app");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// end::class[]
|
// end::class[]
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -31,13 +31,13 @@ import sample.websocket.WebSocketDisconnectHandler;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class WebSocketHandlersConfig<S extends ExpiringSession> {
|
public class WebSocketHandlersConfig<S extends ExpiringSession> {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public WebSocketConnectHandler<S> webSocketConnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
|
public WebSocketConnectHandler<S> webSocketConnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
|
||||||
return new WebSocketConnectHandler<S>(messagingTemplate, repository);
|
return new WebSocketConnectHandler<S>(messagingTemplate, repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public WebSocketDisconnectHandler<S> webSocketDisconnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
|
public WebSocketDisconnectHandler<S> webSocketDisconnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
|
||||||
return new WebSocketDisconnectHandler<S>(messagingTemplate, repository);
|
return new WebSocketDisconnectHandler<S>(messagingTemplate, repository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -26,11 +26,13 @@ import org.springframework.security.config.annotation.web.socket.AbstractSecurit
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
|
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
|
||||||
|
|
||||||
@Override
|
// @formatter:off
|
||||||
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
|
@Override
|
||||||
messages
|
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
|
||||||
.antMatchers(SimpMessageType.MESSAGE,"/queue/**","/topic/**").denyAll()
|
messages
|
||||||
.antMatchers(SimpMessageType.SUBSCRIBE, "/queue/**/*-user*","/topic/**/*-user*").denyAll()
|
.antMatchers(SimpMessageType.MESSAGE,"/queue/**","/topic/**").denyAll()
|
||||||
.anyMessage().authenticated();
|
.antMatchers(SimpMessageType.SUBSCRIBE, "/queue/**/*-user*","/topic/**/*-user*").denyAll()
|
||||||
}
|
.anyMessage().authenticated();
|
||||||
|
}
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.data;
|
package sample.data;
|
||||||
|
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
@@ -10,13 +24,13 @@ import javax.persistence.Id;
|
|||||||
public class ActiveWebSocketUser {
|
public class ActiveWebSocketUser {
|
||||||
@Id
|
@Id
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
private Calendar connectionTime;
|
private Calendar connectionTime;
|
||||||
|
|
||||||
public ActiveWebSocketUser() {}
|
public ActiveWebSocketUser() {}
|
||||||
|
|
||||||
public ActiveWebSocketUser(String id, String username, Calendar connectionTime) {
|
public ActiveWebSocketUser(String id, String username, Calendar connectionTime) {
|
||||||
super();
|
super();
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -39,6 +53,6 @@ public class ActiveWebSocketUser {
|
|||||||
public void setConnectionTime(Calendar connectionTime) {
|
public void setConnectionTime(Calendar connectionTime) {
|
||||||
this.connectionTime = connectionTime;
|
this.connectionTime = connectionTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.data;
|
package sample.data;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -1,14 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package sample.data;
|
package sample.data;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
public class InstantMessage {
|
public class InstantMessage {
|
||||||
private String to;
|
private String to;
|
||||||
|
|
||||||
private String from;
|
private String from;
|
||||||
|
|
||||||
private String message;
|
private String message;
|
||||||
|
|
||||||
private Calendar created = Calendar.getInstance();
|
private Calendar created = Calendar.getInstance();
|
||||||
|
|
||||||
public String getTo() {
|
public String getTo() {
|
||||||
@@ -42,7 +57,7 @@ public class InstantMessage {
|
|||||||
public void setCreated(Calendar created) {
|
public void setCreated(Calendar created) {
|
||||||
this.created = created;
|
this.created = created;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -40,73 +40,73 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
|||||||
@Entity
|
@Entity
|
||||||
public class User implements Serializable {
|
public class User implements Serializable {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@NotEmpty(message = "First name is required.")
|
@NotEmpty(message = "First name is required.")
|
||||||
private String firstName;
|
private String firstName;
|
||||||
|
|
||||||
@NotEmpty(message = "Last name is required.")
|
@NotEmpty(message = "Last name is required.")
|
||||||
private String lastName;
|
private String lastName;
|
||||||
|
|
||||||
@Email(message = "Please provide a valid email address.")
|
@Email(message = "Please provide a valid email address.")
|
||||||
@NotEmpty(message = "Email is required.")
|
@NotEmpty(message = "Email is required.")
|
||||||
@Column(unique=true, nullable = false)
|
@Column(unique=true, nullable = false)
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
@NotEmpty(message = "Password is required.")
|
@NotEmpty(message = "Password is required.")
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
public User() {}
|
public User() {}
|
||||||
|
|
||||||
public User(User user) {
|
public User(User user) {
|
||||||
this.id = user.id;
|
this.id = user.id;
|
||||||
this.firstName = user.firstName;
|
this.firstName = user.firstName;
|
||||||
this.lastName = user.lastName;
|
this.lastName = user.lastName;
|
||||||
this.email = user.email;
|
this.email = user.email;
|
||||||
this.password = user.password;
|
this.password = user.password;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPassword(String password) {
|
public void setPassword(String password) {
|
||||||
this.password = password;
|
this.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(Long id) {
|
public void setId(Long id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
return firstName;
|
return firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFirstName(String firstName) {
|
public void setFirstName(String firstName) {
|
||||||
this.firstName = firstName;
|
this.firstName = firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLastName() {
|
public String getLastName() {
|
||||||
return lastName;
|
return lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLastName(String lastName) {
|
public void setLastName(String lastName) {
|
||||||
this.lastName = lastName;
|
this.lastName = lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEmail() {
|
public String getEmail() {
|
||||||
return email;
|
return email;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEmail(String email) {
|
public void setEmail(String email) {
|
||||||
this.email = email;
|
this.email = email;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final long serialVersionUID = 2738859149330833739L;
|
private static final long serialVersionUID = 2738859149330833739L;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -25,5 +25,5 @@ import org.springframework.data.repository.CrudRepository;
|
|||||||
*/
|
*/
|
||||||
public interface UserRepository extends CrudRepository<User, Long> {
|
public interface UserRepository extends CrudRepository<User, Long> {
|
||||||
|
|
||||||
User findByEmail(String email);
|
User findByEmail(String email);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -22,7 +22,6 @@ import org.springframework.messaging.Message;
|
|||||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||||
import org.springframework.messaging.simp.SimpMessageSendingOperations;
|
import org.springframework.messaging.simp.SimpMessageSendingOperations;
|
||||||
import org.springframework.messaging.simp.annotation.SubscribeMapping;
|
import org.springframework.messaging.simp.annotation.SubscribeMapping;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
@@ -40,29 +39,29 @@ import sample.security.CurrentUser;
|
|||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/")
|
@RequestMapping("/")
|
||||||
public class MessageController {
|
public class MessageController {
|
||||||
private SimpMessageSendingOperations messagingTemplate;
|
private SimpMessageSendingOperations messagingTemplate;
|
||||||
private ActiveWebSocketUserRepository activeUserRepository;
|
private ActiveWebSocketUserRepository activeUserRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public MessageController(ActiveWebSocketUserRepository activeUserRepository,SimpMessageSendingOperations messagingTemplate) {
|
public MessageController(ActiveWebSocketUserRepository activeUserRepository,SimpMessageSendingOperations messagingTemplate) {
|
||||||
this.activeUserRepository = activeUserRepository;
|
this.activeUserRepository = activeUserRepository;
|
||||||
this.messagingTemplate = messagingTemplate;
|
this.messagingTemplate = messagingTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/")
|
@RequestMapping("/")
|
||||||
public String im() {
|
public String im() {
|
||||||
return "index";
|
return "index";
|
||||||
}
|
}
|
||||||
|
|
||||||
@MessageMapping("/im")
|
@MessageMapping("/im")
|
||||||
public void im(InstantMessage im, @CurrentUser User currentUser) {
|
public void im(InstantMessage im, @CurrentUser User currentUser) {
|
||||||
im.setFrom(currentUser.getEmail());
|
im.setFrom(currentUser.getEmail());
|
||||||
messagingTemplate.convertAndSendToUser(im.getTo(),"/queue/messages",im);
|
messagingTemplate.convertAndSendToUser(im.getTo(),"/queue/messages",im);
|
||||||
messagingTemplate.convertAndSendToUser(im.getFrom(),"/queue/messages",im);
|
messagingTemplate.convertAndSendToUser(im.getFrom(),"/queue/messages",im);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeMapping("/users")
|
@SubscribeMapping("/users")
|
||||||
public List<String> subscribeMessages() throws Exception {
|
public List<String> subscribeMessages() throws Exception {
|
||||||
return activeUserRepository.findAllActiveUsers();
|
return activeUserRepository.findAllActiveUsers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -33,55 +33,55 @@ import org.springframework.stereotype.Service;
|
|||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class UserRepositoryUserDetailsService implements UserDetailsService {
|
public class UserRepositoryUserDetailsService implements UserDetailsService {
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public UserRepositoryUserDetailsService(UserRepository userRepository) {
|
public UserRepositoryUserDetailsService(UserRepository userRepository) {
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
|
* @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
|
||||||
*/
|
*/
|
||||||
public UserDetails loadUserByUsername(String username)
|
public UserDetails loadUserByUsername(String username)
|
||||||
throws UsernameNotFoundException {
|
throws UsernameNotFoundException {
|
||||||
User user = userRepository.findByEmail(username);
|
User user = userRepository.findByEmail(username);
|
||||||
if(user == null) {
|
if(user == null) {
|
||||||
throw new UsernameNotFoundException("Could not find user " + username);
|
throw new UsernameNotFoundException("Could not find user " + username);
|
||||||
}
|
}
|
||||||
return new CustomUserDetails(user);
|
return new CustomUserDetails(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static class CustomUserDetails extends User implements UserDetails {
|
private final static class CustomUserDetails extends User implements UserDetails {
|
||||||
|
|
||||||
private CustomUserDetails(User user) {
|
private CustomUserDetails(User user) {
|
||||||
super(user);
|
super(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
return AuthorityUtils.createAuthorityList("ROLE_USER");
|
return AuthorityUtils.createAuthorityList("ROLE_USER");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return getEmail();
|
return getEmail();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAccountNonExpired() {
|
public boolean isAccountNonExpired() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAccountNonLocked() {
|
public boolean isAccountNonLocked() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCredentialsNonExpired() {
|
public boolean isCredentialsNonExpired() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final long serialVersionUID = 5639683223516504866L;
|
private static final long serialVersionUID = 5639683223516504866L;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -29,23 +29,23 @@ import sample.data.ActiveWebSocketUser;
|
|||||||
import sample.data.ActiveWebSocketUserRepository;
|
import sample.data.ActiveWebSocketUserRepository;
|
||||||
|
|
||||||
public class WebSocketConnectHandler<S> implements ApplicationListener<SessionConnectEvent> {
|
public class WebSocketConnectHandler<S> implements ApplicationListener<SessionConnectEvent> {
|
||||||
private ActiveWebSocketUserRepository repository;
|
private ActiveWebSocketUserRepository repository;
|
||||||
private SimpMessageSendingOperations messagingTemplate;
|
private SimpMessageSendingOperations messagingTemplate;
|
||||||
|
|
||||||
public WebSocketConnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
|
public WebSocketConnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
|
||||||
super();
|
super();
|
||||||
this.messagingTemplate = messagingTemplate;
|
this.messagingTemplate = messagingTemplate;
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onApplicationEvent(SessionConnectEvent event) {
|
public void onApplicationEvent(SessionConnectEvent event) {
|
||||||
MessageHeaders headers = event.getMessage().getHeaders();
|
MessageHeaders headers = event.getMessage().getHeaders();
|
||||||
Principal user = SimpMessageHeaderAccessor.getUser(headers);
|
Principal user = SimpMessageHeaderAccessor.getUser(headers);
|
||||||
if(user == null) {
|
if(user == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String id = SimpMessageHeaderAccessor.getSessionId(headers);
|
String id = SimpMessageHeaderAccessor.getSessionId(headers);
|
||||||
repository.save(new ActiveWebSocketUser(id, user.getName(), Calendar.getInstance()));
|
repository.save(new ActiveWebSocketUser(id, user.getName(), Calendar.getInstance()));
|
||||||
messagingTemplate.convertAndSend("/topic/friends/signin", Arrays.asList(user.getName()));
|
messagingTemplate.convertAndSend("/topic/friends/signin", Arrays.asList(user.getName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -25,27 +25,27 @@ import sample.data.ActiveWebSocketUser;
|
|||||||
import sample.data.ActiveWebSocketUserRepository;
|
import sample.data.ActiveWebSocketUserRepository;
|
||||||
|
|
||||||
public class WebSocketDisconnectHandler<S> implements ApplicationListener<SessionDisconnectEvent> {
|
public class WebSocketDisconnectHandler<S> implements ApplicationListener<SessionDisconnectEvent> {
|
||||||
private ActiveWebSocketUserRepository repository;
|
private ActiveWebSocketUserRepository repository;
|
||||||
private SimpMessageSendingOperations messagingTemplate;
|
private SimpMessageSendingOperations messagingTemplate;
|
||||||
|
|
||||||
public WebSocketDisconnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
|
public WebSocketDisconnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
|
||||||
super();
|
super();
|
||||||
this.messagingTemplate = messagingTemplate;
|
this.messagingTemplate = messagingTemplate;
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onApplicationEvent(SessionDisconnectEvent event) {
|
public void onApplicationEvent(SessionDisconnectEvent event) {
|
||||||
String id = event.getSessionId();
|
String id = event.getSessionId();
|
||||||
if(id == null) {
|
if(id == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ActiveWebSocketUser user = repository.findOne(id);
|
ActiveWebSocketUser user = repository.findOne(id);
|
||||||
if(user == null) {
|
if(user == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
repository.delete(id);
|
repository.delete(id);
|
||||||
messagingTemplate.convertAndSend("/topic/friends/signout", Arrays.asList(user.getUsername()));
|
messagingTemplate.convertAndSend("/topic/friends/signout", Arrays.asList(user.getUsername()));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,139 +1,139 @@
|
|||||||
|
|
||||||
function ApplicationModel(stompClient) {
|
function ApplicationModel(stompClient) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
self.friends = ko.observableArray();
|
self.friends = ko.observableArray();
|
||||||
self.username = ko.observable();
|
self.username = ko.observable();
|
||||||
self.conversation = ko.observable(new ImConversationModel(stompClient,this.username));
|
self.conversation = ko.observable(new ImConversationModel(stompClient,this.username));
|
||||||
self.notifications = ko.observableArray();
|
self.notifications = ko.observableArray();
|
||||||
|
|
||||||
self.connect = function() {
|
self.connect = function() {
|
||||||
stompClient.connect({}, function(frame) {
|
stompClient.connect({}, function(frame) {
|
||||||
|
|
||||||
console.log('Connected ' + frame);
|
console.log('Connected ' + frame);
|
||||||
self.username(frame.headers['user-name']);
|
self.username(frame.headers['user-name']);
|
||||||
|
|
||||||
// self.friendSignin({"username": "luke"});
|
// self.friendSignin({"username": "luke"});
|
||||||
|
|
||||||
stompClient.subscribe("/user/queue/errors", function(message) {
|
stompClient.subscribe("/user/queue/errors", function(message) {
|
||||||
self.pushNotification("Error " + message.body);
|
self.pushNotification("Error " + message.body);
|
||||||
});
|
});
|
||||||
stompClient.subscribe("/app/users", function(message) {
|
stompClient.subscribe("/app/users", function(message) {
|
||||||
var friends = JSON.parse(message.body);
|
var friends = JSON.parse(message.body);
|
||||||
|
|
||||||
for(var i=0;i<friends.length;i++) {
|
for(var i=0;i<friends.length;i++) {
|
||||||
self.friendSignin({"username": friends[i]});
|
self.friendSignin({"username": friends[i]});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
stompClient.subscribe("/topic/friends/signin", function(message) {
|
stompClient.subscribe("/topic/friends/signin", function(message) {
|
||||||
var friends = JSON.parse(message.body);
|
var friends = JSON.parse(message.body);
|
||||||
|
|
||||||
for(var i=0;i<friends.length;i++) {
|
for(var i=0;i<friends.length;i++) {
|
||||||
self.friendSignin(new ImFriend({"username": friends[i]}));
|
self.friendSignin(new ImFriend({"username": friends[i]}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
stompClient.subscribe("/topic/friends/signout", function(message) {
|
stompClient.subscribe("/topic/friends/signout", function(message) {
|
||||||
var friends = JSON.parse(message.body);
|
var friends = JSON.parse(message.body);
|
||||||
|
|
||||||
for(var i=0;i<friends.length;i++) {
|
for(var i=0;i<friends.length;i++) {
|
||||||
self.friendSignout(new ImFriend({"username": friends[i]}));
|
self.friendSignout(new ImFriend({"username": friends[i]}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
stompClient.subscribe("/user/queue/messages", function(message) {
|
stompClient.subscribe("/user/queue/messages", function(message) {
|
||||||
self.conversation().receiveMessage(JSON.parse(message.body));
|
self.conversation().receiveMessage(JSON.parse(message.body));
|
||||||
});
|
});
|
||||||
}, function(error) {
|
}, function(error) {
|
||||||
self.pushNotification(error)
|
self.pushNotification(error)
|
||||||
console.log("STOMP protocol error " + error);
|
console.log("STOMP protocol error " + error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pushNotification = function(text) {
|
self.pushNotification = function(text) {
|
||||||
self.notifications.push({notification: text});
|
self.notifications.push({notification: text});
|
||||||
if (self.notifications().length > 5) {
|
if (self.notifications().length > 5) {
|
||||||
self.notifications.shift();
|
self.notifications.shift();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.logout = function() {
|
self.logout = function() {
|
||||||
stompClient.disconnect();
|
stompClient.disconnect();
|
||||||
window.location.href = "../logout.html";
|
window.location.href = "../logout.html";
|
||||||
}
|
}
|
||||||
|
|
||||||
self.friendSignin = function(friend) {
|
self.friendSignin = function(friend) {
|
||||||
self.friends.push(friend);
|
self.friends.push(friend);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.friendSignout = function(friend) {
|
self.friendSignout = function(friend) {
|
||||||
var r = self.friends.remove(
|
var r = self.friends.remove(
|
||||||
function(item) {
|
function(item) {
|
||||||
item.username == friend.username
|
item.username == friend.username
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
self.friends(r);
|
self.friends(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ImFriend(data) {
|
function ImFriend(data) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
self.username = data.username;
|
self.username = data.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ImConversationModel(stompClient,from) {
|
function ImConversationModel(stompClient,from) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.stompClient = stompClient;
|
self.stompClient = stompClient;
|
||||||
self.from = from;
|
self.from = from;
|
||||||
self.to = ko.observable(new ImFriend('null'));
|
self.to = ko.observable(new ImFriend('null'));
|
||||||
self.draft = ko.observable('')
|
self.draft = ko.observable('')
|
||||||
|
|
||||||
self.messages = ko.observableArray();
|
self.messages = ko.observableArray();
|
||||||
|
|
||||||
self.receiveMessage = function(message) {
|
self.receiveMessage = function(message) {
|
||||||
var elem = $('#chat');
|
var elem = $('#chat');
|
||||||
var isFromSelf = self.from() == message.from;
|
var isFromSelf = self.from() == message.from;
|
||||||
var isFromTo = self.to().username == message.from;
|
var isFromTo = self.to().username == message.from;
|
||||||
if(!(isFromTo || isFromSelf)) {
|
if(!(isFromTo || isFromSelf)) {
|
||||||
self.chat(new ImFriend({"username":message.from}))
|
self.chat(new ImFriend({"username":message.from}))
|
||||||
}
|
}
|
||||||
|
|
||||||
var atBottom = (elem[0].scrollHeight - elem.scrollTop() == elem.outerHeight());
|
var atBottom = (elem[0].scrollHeight - elem.scrollTop() == elem.outerHeight());
|
||||||
|
|
||||||
self.messages.push(new ImModel(message));
|
self.messages.push(new ImModel(message));
|
||||||
|
|
||||||
if (atBottom)
|
if (atBottom)
|
||||||
elem.scrollTop(elem[0].scrollHeight);
|
elem.scrollTop(elem[0].scrollHeight);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.chat = function(to) {
|
self.chat = function(to) {
|
||||||
self.to(to);
|
self.to(to);
|
||||||
self.draft('');
|
self.draft('');
|
||||||
self.messages.removeAll()
|
self.messages.removeAll()
|
||||||
$('#trade-dialog').modal();
|
$('#trade-dialog').modal();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.send = function() {
|
self.send = function() {
|
||||||
var data = {
|
var data = {
|
||||||
"created" : new Date(),
|
"created" : new Date(),
|
||||||
"from" : self.from(),
|
"from" : self.from(),
|
||||||
"to" : self.to().username,
|
"to" : self.to().username,
|
||||||
"message" : self.draft()
|
"message" : self.draft()
|
||||||
};
|
};
|
||||||
var destination = "/app/im"; // /queue/messages-user1
|
var destination = "/app/im"; // /queue/messages-user1
|
||||||
stompClient.send(destination, {}, JSON.stringify(data));
|
stompClient.send(destination, {}, JSON.stringify(data));
|
||||||
self.draft('');
|
self.draft('');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function ImModel(data) {
|
function ImModel(data) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
self.created = new Date(data.created);
|
self.created = new Date(data.created);
|
||||||
self.to = data.to;
|
self.to = data.to;
|
||||||
self.message = data.message;
|
self.message = data.message;
|
||||||
self.from = data.from;
|
self.from = data.from;
|
||||||
self.messageFormatted = ko.computed(function() {
|
self.messageFormatted = ko.computed(function() {
|
||||||
return self.created.getHours() + ":" + self.created.getMinutes() + ":" + self.created.getSeconds() + " - " + self.from + " - " + self.message;
|
return self.created.getHours() + ":" + self.created.getMinutes() + ":" + self.created.getSeconds() + " - " + self.from + " - " + self.message;
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,74 +1,74 @@
|
|||||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="layout">
|
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="layout">
|
||||||
<head>
|
<head>
|
||||||
<title>View All</title>
|
<title>View All</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div layout:fragment="content">
|
<div layout:fragment="content">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div id="heading" class="masthead">
|
<div id="heading" class="masthead">
|
||||||
<h3 class="muted">Chat Application</h3>
|
<h3 class="muted">Chat Application</h3>
|
||||||
</div>
|
</div>
|
||||||
<div id="main-content">
|
<div id="main-content">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>User</th>
|
<th>User</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody data-bind="foreach: friends()">
|
<tbody data-bind="foreach: friends()">
|
||||||
<tr>
|
<tr>
|
||||||
<td data-bind="text: username"></td>
|
<td data-bind="text: username"></td>
|
||||||
<td class="trade-buttons">
|
<td class="trade-buttons">
|
||||||
<button class="btn btn-primary" data-bind="click: $root.conversation().chat">Chat</button>
|
<button class="btn btn-primary" data-bind="click: $root.conversation().chat">Chat</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div id="trade-dialog" class="modal hide fade" tabindex="-1">
|
<div id="trade-dialog" class="modal hide fade" tabindex="-1">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div>Chat with <span data-bind="text: conversation().to().username"></span></div>
|
<div>Chat with <span data-bind="text: conversation().to().username"></span></div>
|
||||||
<div id="chat" style="height: 5em;max-height: 200px;overflow:scroll" data-bind="foreach: conversation().messages()">
|
<div id="chat" style="height: 5em;max-height: 200px;overflow:scroll" data-bind="foreach: conversation().messages()">
|
||||||
<div data-bind="text: messageFormatted"></div>
|
<div data-bind="text: messageFormatted"></div>
|
||||||
</div>
|
</div>
|
||||||
<form class="form-horizontal" data-bind="submit: conversation().send">
|
<form class="form-horizontal" data-bind="submit: conversation().send">
|
||||||
<textarea data-bind="value: conversation().draft"></textarea>
|
<textarea data-bind="value: conversation().draft"></textarea>
|
||||||
<button class="btn btn-primary" type="submit">Send</button>
|
<button class="btn btn-primary" type="submit">Send</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
<h5>Notifications</h5>
|
<h5>Notifications</h5>
|
||||||
<ul data-bind="foreach: notifications">
|
<ul data-bind="foreach: notifications">
|
||||||
<li data-bind="text: notification"></li>
|
<li data-bind="text: notification"></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- 3rd party -->
|
<!-- 3rd party -->
|
||||||
<script src="resources/js/jquery/jquery.js"></script>
|
<script src="resources/js/jquery/jquery.js"></script>
|
||||||
<script src="resources/js/bootstrap/js/bootstrap.js"></script>
|
<script src="resources/js/bootstrap/js/bootstrap.js"></script>
|
||||||
<script src="resources/js/knockout/knockout.js"></script>
|
<script src="resources/js/knockout/knockout.js"></script>
|
||||||
<script src="resources/js/sockjs/sockjs.js"></script>
|
<script src="resources/js/sockjs/sockjs.js"></script>
|
||||||
<script src="resources/js/stomp/lib/stomp.js"></script>
|
<script src="resources/js/stomp/lib/stomp.js"></script>
|
||||||
|
|
||||||
<!-- application -->
|
<!-- application -->
|
||||||
<script src="resources/js/message.js"></script>
|
<script src="resources/js/message.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function() {
|
(function() {
|
||||||
var socket = new SockJS('/messages');
|
var socket = new SockJS('/messages');
|
||||||
var stompClient = Stomp.over(socket);
|
var stompClient = Stomp.over(socket);
|
||||||
|
|
||||||
var appModel = new ApplicationModel(stompClient);
|
var appModel = new ApplicationModel(stompClient);
|
||||||
ko.applyBindings(appModel);
|
ko.applyBindings(appModel);
|
||||||
|
|
||||||
appModel.connect();
|
appModel.connect();
|
||||||
|
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,124 +1,124 @@
|
|||||||
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd">
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd">
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
xmlns:th="http://www.thymeleaf.org"
|
xmlns:th="http://www.thymeleaf.org"
|
||||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
||||||
<head>
|
<head>
|
||||||
<title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">SecureMail</title>
|
<title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">SecureMail</title>
|
||||||
<link rel="icon" type="image/x-icon" th:href="@{/resources/img/favicon.ico}" href="../static/img/favicon.ico"/>
|
<link rel="icon" type="image/x-icon" th:href="@{/resources/img/favicon.ico}" href="../static/img/favicon.ico"/>
|
||||||
<link th:href="@{/resources/css/bootstrap.css}" href="../static/css/bootstrap.css" rel="stylesheet"></link>
|
<link th:href="@{/resources/css/bootstrap.css}" href="../static/css/bootstrap.css" rel="stylesheet"></link>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
/* Sticky footer styles
|
/* Sticky footer styles
|
||||||
-------------------------------------------------- */
|
-------------------------------------------------- */
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
/* The html and body elements cannot have any padding or margin. */
|
/* The html and body elements cannot have any padding or margin. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wrapper for page content to push down footer */
|
/* Wrapper for page content to push down footer */
|
||||||
#wrap {
|
#wrap {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
/* Negative indent footer by it's height */
|
/* Negative indent footer by it's height */
|
||||||
margin: 0 auto -60px;
|
margin: 0 auto -60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the fixed height of the footer here */
|
/* Set the fixed height of the footer here */
|
||||||
#push,
|
#push,
|
||||||
#footer {
|
#footer {
|
||||||
height: 60px;
|
height: 60px;
|
||||||
}
|
}
|
||||||
#footer {
|
#footer {
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lastly, apply responsive CSS fixes as necessary */
|
/* Lastly, apply responsive CSS fixes as necessary */
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
#footer {
|
#footer {
|
||||||
margin-left: -20px;
|
margin-left: -20px;
|
||||||
margin-right: -20px;
|
margin-right: -20px;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Custom page CSS
|
/* Custom page CSS
|
||||||
-------------------------------------------------- */
|
-------------------------------------------------- */
|
||||||
/* Not required for template or sticky footer method. */
|
/* Not required for template or sticky footer method. */
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
width: auto;
|
width: auto;
|
||||||
max-width: 680px;
|
max-width: 680px;
|
||||||
}
|
}
|
||||||
.container .credit {
|
.container .credit {
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
color: green;
|
color: green;
|
||||||
}
|
}
|
||||||
.navbar-form {
|
.navbar-form {
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<link th:href="@{resources/css/bootstrap-responsive.css}" href="/static/css/bootstrap-responsive.css" rel="stylesheet"></link>
|
<link th:href="@{resources/css/bootstrap-responsive.css}" href="/static/css/bootstrap-responsive.css" rel="stylesheet"></link>
|
||||||
|
|
||||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="wrap">
|
<div id="wrap">
|
||||||
<div class="navbar navbar-inverse navbar-static-top">
|
<div class="navbar navbar-inverse navbar-static-top">
|
||||||
<div class="navbar-inner">
|
<div class="navbar-inner">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="brand" th:href="@{/}"><img th:src="@{/resources/img/logo.png}" alt="Spring Security Sample"/></a>
|
<a class="brand" th:href="@{/}"><img th:src="@{/resources/img/logo.png}" alt="Spring Security Sample"/></a>
|
||||||
|
|
||||||
<div class="nav-collapse collapse"
|
<div class="nav-collapse collapse"
|
||||||
th:with="currentUser=${#httpServletRequest.userPrincipal?.principal}">
|
th:with="currentUser=${#httpServletRequest.userPrincipal?.principal}">
|
||||||
<div th:if="${currentUser != null}">
|
<div th:if="${currentUser != null}">
|
||||||
<form class="navbar-form pull-right" th:action="@{/logout}" method="post">
|
<form class="navbar-form pull-right" th:action="@{/logout}" method="post">
|
||||||
<input type="submit" value="Log out" />
|
<input type="submit" value="Log out" />
|
||||||
</form>
|
</form>
|
||||||
<p class="navbar-text pull-right" th:text="${currentUser.firstName}">
|
<p class="navbar-text pull-right" th:text="${currentUser.firstName}">
|
||||||
sample_user
|
sample_user
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li><a th:href="@{/}">IM</a></li>
|
<li><a th:href="@{/}">IM</a></li>
|
||||||
<li><a th:href="@{/h2/}">H2</a></li>
|
<li><a th:href="@{/h2/}">H2</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Begin page content -->
|
<!-- Begin page content -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="alert alert-success"
|
<div class="alert alert-success"
|
||||||
th:if="${globalMessage}"
|
th:if="${globalMessage}"
|
||||||
th:text="${globalMessage}">
|
th:text="${globalMessage}">
|
||||||
Some Success message
|
Some Success message
|
||||||
</div>
|
</div>
|
||||||
<div layout:fragment="content">
|
<div layout:fragment="content">
|
||||||
Fake content
|
Fake content
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="push"><!-- --></div>
|
<div id="push"><!-- --></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="muted credit">Visit the <a href="http://spring.io/spring-security">Spring Security</a> site for more <a href="https://github.com/spring-projects/spring-security/blob/master/samples/">samples</a>.</p>
|
<p class="muted credit">Visit the <a href="http://spring.io/spring-security">Spring Security</a> site for more <a href="https://github.com/spring-projects/spring-security/blob/master/samples/">samples</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user