Spaces to tabs and license cleanup

This commit is contained in:
Rob Winch
2015-04-02 15:57:00 -05:00
parent 93b8856a20
commit 4dedb4d10a
143 changed files with 8270 additions and 7654 deletions

View File

@@ -1,14 +1,14 @@
buildscript {
repositories {
maven { url "https://repo.spring.io/plugins-release" }
}
dependencies {
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.7")
classpath("org.springframework.build.gradle:spring-io-plugin:0.0.3.RELEASE")
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2'
}
repositories {
maven { url "https://repo.spring.io/plugins-release" }
}
dependencies {
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.7")
classpath("org.springframework.build.gradle:spring-io-plugin:0.0.3.RELEASE")
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2'
}
}
group = 'org.springframework.session'
@@ -27,37 +27,37 @@ apply plugin: 'base'
sonarRunner {
sonarProperties {
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.projectName", "Spring Session"
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
property "sonar.links.homepage", 'https://github.com/spring-projects/spring-session'
property "sonar.links.ci", 'https://build.spring.io/browse/SESSION'
property "sonar.links.issue", 'https://github.com/spring-projects/spring-session/issues'
property "sonar.links.scm", 'https://github.com/spring-projects/spring-session'
property "sonar.links.scm_dev", 'https://github.com/spring-projects/spring-session.git'
property "sonar.java.coveragePlugin", "jacoco"
}
sonarProperties {
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.projectName", "Spring Session"
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
property "sonar.links.homepage", 'https://github.com/spring-projects/spring-session'
property "sonar.links.ci", 'https://build.spring.io/browse/SESSION'
property "sonar.links.issue", 'https://github.com/spring-projects/spring-session/issues'
property "sonar.links.scm", 'https://github.com/spring-projects/spring-session'
property "sonar.links.scm_dev", 'https://github.com/spring-projects/spring-session.git'
property "sonar.java.coveragePlugin", "jacoco"
}
}
task configDocsZip(dependsOn: [':docs:asciidoctor',':spring-session:javadoc']) << {
project.tasks.docsZip.from(project(':docs').asciidoctor) {
into('reference')
}
project.tasks.docsZip.from(project(':spring-session').javadoc) {
into('api')
}
project.tasks.docsZip.from(project(':docs').asciidoctor) {
into('reference')
}
project.tasks.docsZip.from(project(':spring-session').javadoc) {
into('api')
}
}
task docsZip(type: Zip, dependsOn: 'configDocsZip') {
group = "Distribution"
baseName = "spring-session"
classifier = "docs"
description = "Builds -${classifier} archive containing api and reference " +
"for deployment."
group = "Distribution"
baseName = "spring-session"
classifier = "docs"
description = "Builds -${classifier} archive containing api and reference " +
"for deployment."
}
artifacts {
archives docsZip
archives docsZip
}

View File

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

View File

@@ -16,14 +16,14 @@ If you are using Maven, ensure to add the following dependencies:
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
</dependencies>
----
@@ -36,12 +36,12 @@ Ensure you have the following in your pom.xml:
----
<repositories>
<!-- ... -->
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
endif::[]
@@ -54,8 +54,8 @@ Ensure you have the following in your pom.xml:
[source,xml]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
@@ -69,7 +69,7 @@ Add the following Spring Configuration:
[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.
@@ -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:
$ ./gradlew :samples:boot:bootRun
----
$ ./gradlew :samples:boot:bootRun
----
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:
$ redis-cli keys '*' | xargs redis-cli del
$ redis-cli keys '*' | xargs redis-cli del
TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli].
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.

View File

@@ -15,19 +15,19 @@ If you are using Maven, ensure to add the following dependencies:
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
</dependencies>
----
@@ -40,12 +40,12 @@ Ensure you have the following in your pom.xml:
----
<repositories>
<!-- ... -->
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
endif::[]
@@ -58,8 +58,8 @@ Ensure you have the following in your pom.xml:
[source,xml]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
@@ -75,7 +75,7 @@ Add the following Spring Configuration:
[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.
@@ -100,7 +100,7 @@ You can find an example below:
.src/main/java/sample/Initializer.java
[source,java]
----
include::{samples-dir}httpsession/src/main/java/sample/Initializer.java[]
include::{samples-dir}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`.
@@ -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:
$ ./gradlew :samples:httpsession:tomcatRun
----
$ ./gradlew :samples:httpsession:tomcatRun
----
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
[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.
@@ -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:
$ redis-cli keys '*' | xargs redis-cli del
$ redis-cli keys '*' | xargs redis-cli del
TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli].
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.

View File

@@ -15,19 +15,19 @@ If you are using Maven, ensure to add the following dependencies:
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
</dependencies>
----
@@ -40,12 +40,12 @@ Ensure you have the following in your pom.xml:
----
<repositories>
<!-- ... -->
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
endif::[]
@@ -58,8 +58,8 @@ Ensure you have the following in your pom.xml:
[source,xml]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
@@ -75,7 +75,7 @@ Add the following Spring Configuration:
[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.
@@ -107,7 +107,7 @@ Fortunately, Spring Session provides a utility class named `AbstractHttpSessionA
.src/main/java/sample/Initializer.java
[source,java]
----
include::{samples-dir}rest/src/main/java/sample/Initializer.java[]
include::{samples-dir}rest/src/main/java/sample/Initializer.java[tags=class]
----
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
@@ -121,7 +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:
$ ./gradlew :samples:rest:tomcatRun
----
$ ./gradlew :samples:rest:tomcatRun
----
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/
$ curl -v http://localhost:8080/
$ curl -v http://localhost:8080/
Observe that we are prompted for basic authentication. Provide the following information for the username and password:
* **Username** *user*
* **Password** *password*
$ curl -v http://localhost:8080/ -u user:password
$ curl -v http://localhost:8080/ -u user:password
In the output you will notice the following:
@@ -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:
$ curl -v http://localhost:8080/ -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
$ curl -v http://localhost:8080/ -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
The only difference is that the session id is not provided in the response headers because we are reusing an existing session.
If we invalidate the session, then the x-auth-token is displayed in the response with an empty value. For example, the following will invalidate our session:
$ 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.
@@ -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:
$ curl -v http://localhost:8080/ -u user:password
$ curl -v http://localhost:8080/ -u user:password
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:
$ redis-cli keys '*' | xargs redis-cli del
$ redis-cli keys '*' | xargs redis-cli del
TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli].
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
We can now use the *x-auth-token* to make another request with the session we deleted and observe we are prompted for a authentication. For example, the following returns an HTTP 401:
$ curl -v http://localhost:8080/ -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
$ curl -v http://localhost:8080/ -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"

View File

@@ -16,19 +16,19 @@ If you are using Maven, ensure to add the following dependencies:
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom<type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
</dependencies>
----
@@ -41,12 +41,12 @@ Ensure you have the following in your pom.xml:
----
<repositories>
<!-- ... -->
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
endif::[]
@@ -59,8 +59,8 @@ Ensure you have the following in your pom.xml:
[source,xml]
----
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
----
endif::[]
@@ -74,7 +74,7 @@ Add the following Spring Configuration:
[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.
@@ -97,7 +97,7 @@ Since our application is already loading Spring configuration using our `Securit
.src/main/java/sample/SecurityInitializer.java
[source,java]
----
include::{samples-dir}security/src/main/java/sample/SecurityInitializer.java[]
include::{samples-dir}security/src/main/java/sample/SecurityInitializer.java[tags=class]
----
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
@@ -109,7 +109,7 @@ You can find an example below:
.src/main/java/sample/Initializer.java
[source,java]
----
include::{samples-dir}security/src/main/java/sample/Initializer.java[]
include::{samples-dir}security/src/main/java/sample/Initializer.java[tags=class]
----
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
@@ -125,7 +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:
$ ./gradlew :samples:security:tomcatRun
----
$ ./gradlew :samples:security:tomcatRun
----
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:
$ redis-cli keys '*' | xargs redis-cli del
$ redis-cli keys '*' | xargs redis-cli del
TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli].
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.

View File

@@ -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:
$ ./gradlew :samples:users:tomcatRun
----
$ ./gradlew :samples:users:tomcatRun
----
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.
For example, the SESSION cookie might have a value of:
7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
=== 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:
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:

View File

@@ -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/

View File

@@ -106,19 +106,19 @@ It looks something like the following:
----
public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
public SessionRepositoryRequestWrapper(HttpServletRequest original) {
super(original);
}
public SessionRepositoryRequestWrapper(HttpServletRequest original) {
super(original);
}
public HttpSession getSession() {
return getSession(true);
}
public HttpSession getSession() {
return getSession(true);
}
public HttpSession getSession(boolean createNew) {
// create an HttpSession implementation from Spring Session
}
public HttpSession getSession(boolean createNew) {
// create an HttpSession implementation from Spring Session
}
// ... other methods delegate to the original HttpServletRequest ...
// ... other methods delegate to the original HttpServletRequest ...
}
----
@@ -132,15 +132,15 @@ The pseudocode can be found below:
----
public class SessionRepositoryFilter implements Filter {
public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
SessionRepositoryRequestWrapper customRequest =
new SessionRepositoryRequestWrapper(httpRequest);
public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
SessionRepositoryRequestWrapper customRequest =
new SessionRepositoryRequestWrapper(httpRequest);
chain.doFilter(customRequest, response, chain);
}
chain.doFilter(customRequest, response, chain);
}
// ...
// ...
}
----
@@ -286,9 +286,9 @@ Each session is stored in Redis as a Hash.
Each session is set and updated using the HMSET command.
An example of how each session is stored can be seen below.
HMSET spring:session:sessions:<session-id> creationTime 1404360000000 \
maxInactiveInterval 1800 lastAccessedTime 1404360000000 \
sessionAttr:<attrName> someAttrValue sessionAttr:<attrName2> someAttrValue2
HMSET spring:session:sessions:<session-id> creationTime 1404360000000 \
maxInactiveInterval 1800 lastAccessedTime 1404360000000 \
sessionAttr:<attrName> someAttrValue sessionAttr:<attrName2> someAttrValue2
[[api-redisoperationssessionrepository-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().
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>>.
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.
For example:
SADD spring:session:expirations:<expire-rounded-up-to-nearest-minute> <session-id>
EXPIRE spring:session:expirations:<expire-rounded-up-to-nearest-minute> 1800
SADD spring:session:expirations:<expire-rounded-up-to-nearest-minute> <session-id>
EXPIRE spring:session:expirations:<expire-rounded-up-to-nearest-minute> 1800
The background task will then use these mappings to explicitly request each key.
By accessing they key, rather than deleting it, we ensure that Redis deletes the key for us only if the TTL is expired.
@@ -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.
The following would be executed upon saving:
HMSET spring:session:sessions:<session-id> sessionAttr:<attrName2> newValue
EXPIRE spring:session:sessions:<session-id> 1800
HMSET spring:session:sessions:<session-id> sessionAttr:<attrName2> newValue
EXPIRE spring:session:sessions:<session-id> 1800
[[api-redisoperationssessionrepository-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.
To run it use the following:
./gradlew :samples:hazelcast:tomcatRun
./gradlew :samples:hazelcast:tomcatRun
[[community]]
== Spring Session Community

View File

@@ -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
* 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
*/
public class IndexDocTests {
static final String ATTR_USER = "user";
static final String ATTR_USER = "user";
@Test
public void repositoryDemo() {
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
demo.repository = new MapSessionRepository();
@Test
public void repositoryDemo() {
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
demo.repository = new MapSessionRepository();
demo.demo();
}
demo.demo();
}
// tag::repository-demo[]
public class RepositoryDemo<S extends Session> {
private SessionRepository<S> repository; // <1>
// tag::repository-demo[]
public class RepositoryDemo<S extends Session> {
private SessionRepository<S> repository; // <1>
public void demo() {
S toSave = repository.createSession(); // <2>
public void demo() {
S toSave = repository.createSession(); // <2>
// <3>
User rwinch = new User("rwinch");
toSave.setAttribute(ATTR_USER, rwinch);
// <3>
User rwinch = new User("rwinch");
toSave.setAttribute(ATTR_USER, rwinch);
repository.save(toSave); // <4>
repository.save(toSave); // <4>
S session = repository.getSession(toSave.getId()); // <5>
S session = repository.getSession(toSave.getId()); // <5>
// <6>
User user = session.getAttribute(ATTR_USER);
assertThat(user).isEqualTo(rwinch);
}
// <6>
User user = session.getAttribute(ATTR_USER);
assertThat(user).isEqualTo(rwinch);
}
// ... setter methods ...
}
// end::repository-demo[]
// ... setter methods ...
}
// end::repository-demo[]
@Test
public void expireRepositoryDemo() {
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
demo.repository = new MapSessionRepository();
@Test
public void expireRepositoryDemo() {
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
demo.repository = new MapSessionRepository();
demo.demo();
}
demo.demo();
}
// tag::expire-repository-demo[]
public class ExpiringRepositoryDemo<S extends ExpiringSession> {
private SessionRepository<S> repository; // <1>
// tag::expire-repository-demo[]
public class ExpiringRepositoryDemo<S extends ExpiringSession> {
private SessionRepository<S> repository; // <1>
public void demo() {
S toSave = repository.createSession(); // <2>
// ...
toSave.setMaxInactiveIntervalInSeconds(30); // <3>
public void demo() {
S toSave = repository.createSession(); // <2>
// ...
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 ...
}
// end::expire-repository-demo[]
// ... setter methods ...
}
// end::expire-repository-demo[]
@Test
public void newRedisOperationsSessionRepository() {
// tag::new-redisoperationssessionrepository[]
JedisConnectionFactory factory = new JedisConnectionFactory();
SessionRepository<? extends ExpiringSession> repository =
new RedisOperationsSessionRepository(factory);
// end::new-redisoperationssessionrepository[]
}
@Test
public void newRedisOperationsSessionRepository() {
// tag::new-redisoperationssessionrepository[]
JedisConnectionFactory factory = new JedisConnectionFactory();
SessionRepository<? extends ExpiringSession> repository =
new RedisOperationsSessionRepository(factory);
// end::new-redisoperationssessionrepository[]
}
@Test
public void mapRepository() {
// tag::new-mapsessionrepository[]
SessionRepository<? extends ExpiringSession> repository = new MapSessionRepository();
// end::new-mapsessionrepository[]
}
@Test
public void mapRepository() {
// tag::new-mapsessionrepository[]
SessionRepository<? extends ExpiringSession> repository = new MapSessionRepository();
// end::new-mapsessionrepository[]
}
private static class User {
private User(String username) {}
}
private static class User {
private User(String username) {}
}
}

View File

@@ -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
* 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
@EnableWebSocketMessageBroker
public class WebSocketConfig
extends AbstractWebSocketMessageBrokerConfigurer {
extends AbstractWebSocketMessageBrokerConfigurer {
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/messages")
.withSockJS();
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/messages")
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
}
// end::class[]

View File

@@ -1,30 +0,0 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package docs.websocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
/**
* @author Rob Winch
*/
public class WebSocketDocTests {
}

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,10 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
}
}
apply plugin: 'spring-boot'
@@ -16,36 +16,36 @@ tasks.findByPath("artifactoryPublish")?.enabled = false
group = 'samples'
dependencies {
compile project(':spring-session-data-redis'),
"org.springframework.boot:spring-boot-starter-web",
"org.springframework.boot:spring-boot-starter-thymeleaf",
"redis.embedded:embedded-redis:0.2",
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"org.springframework.security:spring-security-config:$springSecurityVersion"
compile project(':spring-session-data-redis'),
"org.springframework.boot:spring-boot-starter-web",
"org.springframework.boot:spring-boot-starter-thymeleaf",
"redis.embedded:embedded-redis:0.2",
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"org.springframework.security:spring-security-config:$springSecurityVersion"
testCompile "org.springframework.boot:spring-boot-starter-test"
testCompile "org.springframework.boot:spring-boot-starter-test"
integrationTestCompile gebDependencies,
"org.spockframework:spock-spring:$spockVersion"
integrationTestCompile gebDependencies,
"org.spockframework:spock-spring:$spockVersion"
}
integrationTest {
doFirst {
def port = reservePort()
doFirst {
def port = reservePort()
def host = 'localhost:' + port
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/'
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
systemProperties['server.port'] = port
systemProperties['management.port'] = 0
}
def host = 'localhost:' + port
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/'
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
systemProperties['server.port'] = port
systemProperties['management.port'] = 0
}
}
def reservePort() {
def socket = new ServerSocket(0)
def result = socket.localPort
socket.close()
result
def socket = new ServerSocket(0)
def result = socket.localPort
socket.close()
result
}

View File

@@ -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
import geb.spock.*
@@ -23,37 +38,37 @@ import pages.*
@IntegrationTest
class BootTests extends GebReportingSpec {
def 'Unauthenticated user sent to log in page'() {
when: 'unauthenticated user request protected page'
via HomePage
then: 'sent to the log in page'
at LoginPage
}
def 'Unauthenticated user sent to log in page'() {
when: 'unauthenticated user request protected page'
via HomePage
then: 'sent to the log in page'
at LoginPage
}
def 'Log in views home page'() {
when: 'log in successfully'
login()
then: 'sent to original page'
at HomePage
and: 'the username is displayed'
username == 'user'
and: 'Spring Session Management is being used'
driver.manage().cookies.find { it.name == 'SESSION' }
and: 'Standard Session is NOT being used'
!driver.manage().cookies.find { it.name == 'JSESSIONID' }
}
def 'Log in views home page'() {
when: 'log in successfully'
login()
then: 'sent to original page'
at HomePage
and: 'the username is displayed'
username == 'user'
and: 'Spring Session Management is being used'
driver.manage().cookies.find { it.name == 'SESSION' }
and: 'Standard Session is NOT being used'
!driver.manage().cookies.find { it.name == 'JSESSIONID' }
}
def 'Log out success'() {
when:
logout()
then:
at LoginPage
}
def 'Log out success'() {
when:
logout()
then:
at LoginPage
}
def 'Logged out user sent to log in page'() {
when: 'logged out user request protected page'
via HomePage
then: 'sent to the log in page'
at LoginPage
}
def 'Logged out user sent to log in page'() {
when: 'logged out user request protected page'
via HomePage
then: 'sent to the log in page'
at LoginPage
}
}

View File

@@ -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
import geb.*
@@ -8,10 +23,10 @@ import geb.*
* @author Rob Winch
*/
class HomePage extends Page {
static url = ''
static at = { assert driver.title == 'Spring Session Sample - Secured Content'; true}
static content = {
username { $('#un').text() }
logout(to:LoginPage) { $('input[type=submit]').click() }
}
static url = ''
static at = { assert driver.title == 'Spring Session Sample - Secured Content'; true}
static content = {
username { $('#un').text() }
logout(to:LoginPage) { $('input[type=submit]').click() }
}
}

View File

@@ -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
import geb.*
@@ -8,15 +23,15 @@ import geb.*
* @author Rob Winch
*/
class LoginPage extends Page {
static url = '/login'
static at = { assert driver.title == 'Login Page'; true}
static content = {
form { $('form') }
submit { $('input[type=submit]') }
login(required:false) { user='user', pass='password' ->
form.username = user
form.password = pass
submit.click(HomePage)
}
}
static url = '/login'
static at = { assert driver.title == 'Login Page'; true}
static content = {
form { $('form') }
submit { $('input[type=submit]') }
login(required:false) { user='user', pass='password' ->
form.username = user
form.password = pass
submit.click(HomePage)
}
}
}

View File

@@ -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
* 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
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -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
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
@@ -36,35 +36,33 @@ import redis.embedded.RedisServer;
@Configuration
public class EmbeddedRedisConfiguration {
@Bean
public static RedisServerBean redisServer() {
return new RedisServerBean();
}
@Bean
public static RedisServerBean redisServer() {
return new RedisServerBean();
}
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void destroy() throws Exception {
if(redisServer != null) {
redisServer.stop();
}
}
public void destroy() throws Exception {
if(redisServer != null) {
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 {}
}
}

View File

@@ -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;
import org.springframework.session.data.redis.config.annotation.web.http.*;
// tag::class[]
@EnableRedisHttpSession // <1>
public class HttpSessionConfig { }
// end::class[]

View File

@@ -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
* 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
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}

View File

@@ -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
* 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
public class IndexController {
@RequestMapping("/")
public String index() {
return "index";
}
@RequestMapping("/")
public String index() {
return "index";
}
}

View File

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

View File

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

View File

@@ -3,17 +3,17 @@ apply from: TOMCAT_GRADLE
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarRunner {
skipProject = true
skipProject = true
}
dependencies {
compile project(':spring-session'),
"com.hazelcast:hazelcast-client:3.3.3",
jstlDependencies
compile project(':spring-session'),
"com.hazelcast:hazelcast-client:3.3.3",
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
}

View File

@@ -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
import geb.spock.*
@@ -12,19 +27,19 @@ import pages.*
*/
@Stepwise
class AttributeTests extends GebReportingSpec {
def 'first visit no attributes'() {
when:
to HomePage
then:
attributes.empty
}
def 'first visit no attributes'() {
when:
to HomePage
then:
attributes.empty
}
def 'create attribute'() {
when:
createAttribute('a','b')
then:
attributes.size() == 1
attributes[0].name == 'a'
attributes[0].value == 'b'
}
def 'create attribute'() {
when:
createAttribute('a','b')
then:
attributes.size() == 1
attributes[0].name == 'a'
attributes[0].value == 'b'
}
}

View File

@@ -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
import geb.*
@@ -8,23 +23,23 @@ import geb.*
* @author Rob Winch
*/
class HomePage extends Page {
static url = ''
static at = { assert driver.title == 'Session Attributes'; true}
static content = {
form { $('form') }
submit { $('input[type=submit]') }
createAttribute(required:false) { name, value ->
form.attributeName = name
form.attributeValue = value
submit.click(HomePage)
}
attributes { moduleList AttributeRow, $("table tr").tail() }
}
static url = ''
static at = { assert driver.title == 'Session Attributes'; true}
static content = {
form { $('form') }
submit { $('input[type=submit]') }
createAttribute(required:false) { name, value ->
form.attributeName = name
form.attributeValue = value
submit.click(HomePage)
}
attributes { moduleList AttributeRow, $("table tr").tail() }
}
}
class AttributeRow extends Module {
static content = {
cell { $("td", it) }
name { cell(0).text() }
value { cell(1).text() }
}
static content = {
cell { $("td", it) }
name { cell(0).text() }
value { cell(1).text() }
}
}

View File

@@ -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 use this file except in compliance with the License.
@@ -40,51 +40,51 @@ import java.util.Map;
@WebListener
public class Initializer implements ServletContextListener {
private HazelcastInstance instance;
private HazelcastInstance instance;
public void contextInitialized(ServletContextEvent sce) {
String sessionMapName = "spring:session:sessions";
ServletContext sc = sce.getServletContext();
public void contextInitialized(ServletContextEvent sce) {
String sessionMapName = "spring:session:sessions";
ServletContext sc = sce.getServletContext();
Config cfg = new Config();
NetworkConfig netConfig = new NetworkConfig();
netConfig.setPort(getAvailablePort());
cfg.setNetworkConfig(netConfig);
SerializerConfig serializer = new SerializerConfig()
.setTypeClass(Object.class)
.setImplementation(new ObjectStreamSerializer());
cfg.getSerializationConfig().addSerializerConfig(serializer);
MapConfig mc = new MapConfig();
mc.setName(sessionMapName);
mc.setTimeToLiveSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
cfg.addMapConfig(mc);
Config cfg = new Config();
NetworkConfig netConfig = new NetworkConfig();
netConfig.setPort(getAvailablePort());
cfg.setNetworkConfig(netConfig);
SerializerConfig serializer = new SerializerConfig()
.setTypeClass(Object.class)
.setImplementation(new ObjectStreamSerializer());
cfg.getSerializationConfig().addSerializerConfig(serializer);
MapConfig mc = new MapConfig();
mc.setName(sessionMapName);
mc.setTimeToLiveSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
cfg.addMapConfig(mc);
instance = Hazelcast.newHazelcastInstance(cfg);
Map<String,ExpiringSession> sessions = instance.getMap(sessionMapName);
instance = Hazelcast.newHazelcastInstance(cfg);
Map<String,ExpiringSession> sessions = instance.getMap(sessionMapName);
SessionRepository<ExpiringSession> sessionRepository =
new MapSessionRepository(sessions);
SessionRepositoryFilter<ExpiringSession> filter =
new SessionRepositoryFilter<ExpiringSession>(sessionRepository);
Dynamic fr = sc.addFilter("springSessionFilter", filter);
fr.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
}
SessionRepository<ExpiringSession> sessionRepository =
new MapSessionRepository(sessions);
SessionRepositoryFilter<ExpiringSession> filter =
new SessionRepositoryFilter<ExpiringSession>(sessionRepository);
Dynamic fr = sc.addFilter("springSessionFilter", filter);
fr.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
}
public void contextDestroyed(ServletContextEvent sce) {
instance.shutdown();
}
public void contextDestroyed(ServletContextEvent sce) {
instance.shutdown();
}
private static int getAvailablePort() {
ServerSocket socket = null;
try {
socket = new ServerSocket(0);
return socket.getLocalPort();
} catch(IOException e) {
throw new RuntimeException(e);
} finally {
try {
socket.close();
}catch(IOException e) {}
}
}
private static int getAvailablePort() {
ServerSocket socket = null;
try {
socket = new ServerSocket(0);
return socket.getLocalPort();
} catch(IOException e) {
throw new RuntimeException(e);
} finally {
try {
socket.close();
}catch(IOException e) {}
}
}
}

View File

@@ -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 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 int getTypeId() {
return 2;
}
public int getTypeId() {
return 2;
}
public void write(ObjectDataOutput objectDataOutput, Object object)
throws IOException {
ObjectOutputStream out = new ObjectOutputStream((OutputStream) objectDataOutput);
out.writeObject(object);
out.flush();
}
public void write(ObjectDataOutput objectDataOutput, Object object)
throws IOException {
ObjectOutputStream out = new ObjectOutputStream((OutputStream) objectDataOutput);
out.writeObject(object);
out.flush();
}
public Object read(ObjectDataInput objectDataInput)
throws IOException {
ObjectInputStream in = new ObjectInputStream((InputStream) objectDataInput);
try {
return in.readObject();
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
}
public Object read(ObjectDataInput objectDataInput)
throws IOException {
ObjectInputStream in = new ObjectInputStream((InputStream) objectDataInput);
try {
return in.readObject();
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
}
public void destroy() {
}
public void destroy() {
}
}

View File

@@ -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
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
@@ -28,13 +28,13 @@ import java.io.IOException;
@WebServlet("/session")
public class SessionServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String attributeName = req.getParameter("attributeName");
String attributeValue = req.getParameter("attributeValue");
req.getSession().setAttribute(attributeName, attributeValue);
resp.sendRedirect(req.getContextPath() + "/");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String attributeName = req.getParameter("attributeName");
String attributeValue = req.getParameter("attributeValue");
req.getSession().setAttribute(attributeName, attributeValue);
resp.sendRedirect(req.getContextPath() + "/");
}
private static final long serialVersionUID = 2878267318695777395L;
private static final long serialVersionUID = 2878267318695777395L;
}

View File

@@ -3,18 +3,18 @@ apply from: TOMCAT_GRADLE
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarRunner {
skipProject = true
skipProject = true
}
dependencies {
compile project(':spring-session-data-redis'),
"org.springframework:spring-web:$springVersion",
"redis.embedded:embedded-redis:0.2",
jstlDependencies
compile project(':spring-session-data-redis'),
"org.springframework:spring-web:$springVersion",
"redis.embedded:embedded-redis:0.2",
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
}

View File

@@ -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
import geb.spock.*
@@ -12,19 +27,19 @@ import pages.*
*/
@Stepwise
class AttributeTests extends GebReportingSpec {
def 'first visit no attributes'() {
when:
to HomePage
then:
attributes.empty
}
def 'first visit no attributes'() {
when:
to HomePage
then:
attributes.empty
}
def 'create attribute'() {
when:
createAttribute('a','b')
then:
attributes.size() == 1
attributes[0].name == 'a'
attributes[0].value == 'b'
}
def 'create attribute'() {
when:
createAttribute('a','b')
then:
attributes.size() == 1
attributes[0].name == 'a'
attributes[0].value == 'b'
}
}

View File

@@ -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
import geb.*
@@ -8,23 +23,23 @@ import geb.*
* @author Rob Winch
*/
class HomePage extends Page {
static url = ''
static at = { assert driver.title == 'Session Attributes'; true}
static content = {
form { $('form') }
submit { $('input[type=submit]') }
createAttribute(required:false) { name, value ->
form.attributeName = name
form.attributeValue = value
submit.click(HomePage)
}
attributes { moduleList AttributeRow, $("table tr").tail() }
}
static url = ''
static at = { assert driver.title == 'Session Attributes'; true}
static content = {
form { $('form') }
submit { $('input[type=submit]') }
createAttribute(required:false) { name, value ->
form.attributeName = name
form.attributeValue = value
submit.click(HomePage)
}
attributes { moduleList AttributeRow, $("table tr").tail() }
}
}
class AttributeRow extends Module {
static content = {
cell { $("td", it) }
name { cell(0).text() }
value { cell(1).text() }
}
static content = {
cell { $("td", it) }
name { cell(0).text() }
value { cell(1).text() }
}
}

View File

@@ -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;
import org.springframework.session.data.redis.config.annotation.web.http.*;

View File

@@ -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
* 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
* the License.
*/
package sample;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
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.embedded.RedisServer;
@@ -34,21 +33,21 @@ import redis.embedded.RedisServer;
* @author Rob Winch
*/
public class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
private RedisServer redisServer;
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void destroy() throws Exception {
if(redisServer != null) {
redisServer.stop();
}
}
public void destroy() throws Exception {
if(redisServer != null) {
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 {}
}

View File

@@ -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;
import javax.servlet.*;
@@ -5,16 +20,19 @@ import javax.servlet.annotation.*;
import javax.servlet.http.*;
import java.io.IOException;
// tag::class[]
@WebServlet("/session")
public class SessionServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String attributeName = req.getParameter("attributeName");
String attributeValue = req.getParameter("attributeValue");
req.getSession().setAttribute(attributeName, attributeValue);
resp.sendRedirect(req.getContextPath() + "/");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String attributeName = req.getParameter("attributeName");
String attributeValue = req.getParameter("attributeValue");
req.getSession().setAttribute(attributeName, attributeValue);
resp.sendRedirect(req.getContextPath() + "/");
}
private static final long serialVersionUID = 2878267318695777395L;
private static final long serialVersionUID = 2878267318695777395L;
}
// end::class[]

View File

@@ -3,18 +3,18 @@ apply from: TOMCAT_GRADLE
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarRunner {
skipProject = true
skipProject = true
}
dependencies {
compile project(':spring-session-data-redis'),
"org.springframework:spring-web:$springVersion",
"redis.embedded:embedded-redis:0.2",
jstlDependencies
compile project(':spring-session-data-redis'),
"org.springframework:spring-web:$springVersion",
"redis.embedded:embedded-redis:0.2",
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
}

View File

@@ -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
import geb.spock.*
@@ -12,19 +27,19 @@ import pages.*
*/
@Stepwise
class AttributeTests extends GebReportingSpec {
def 'first visit no attributes'() {
when:
to HomePage
then:
attributes.empty
}
def 'first visit no attributes'() {
when:
to HomePage
then:
attributes.empty
}
def 'create attribute'() {
when:
createAttribute('a','b')
then:
attributes.size() == 1
attributes[0].name == 'a'
attributes[0].value == 'b'
}
def 'create attribute'() {
when:
createAttribute('a','b')
then:
attributes.size() == 1
attributes[0].name == 'a'
attributes[0].value == 'b'
}
}

View File

@@ -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
import geb.*
@@ -8,23 +23,23 @@ import geb.*
* @author Rob Winch
*/
class HomePage extends Page {
static url = ''
static at = { assert driver.title == 'Session Attributes'; true}
static content = {
form { $('form') }
submit { $('input[type=submit]') }
createAttribute(required:false) { name, value ->
form.attributeName = name
form.attributeValue = value
submit.click(HomePage)
}
attributes { moduleList AttributeRow, $("table tr").tail() }
}
static url = ''
static at = { assert driver.title == 'Session Attributes'; true}
static content = {
form { $('form') }
submit { $('input[type=submit]') }
createAttribute(required:false) { name, value ->
form.attributeName = name
form.attributeValue = value
submit.click(HomePage)
}
attributes { moduleList AttributeRow, $("table tr").tail() }
}
}
class AttributeRow extends Module {
static content = {
cell { $("td", it) }
name { cell(0).text() }
value { cell(1).text() }
}
static content = {
cell { $("td", it) }
name { cell(0).text() }
value { cell(1).text() }
}
}

View File

@@ -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;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.jedis.*;
import org.springframework.session.data.redis.config.annotation.web.http.*;
// tag::class[]
@Import(EmbeddedRedisConfiguration.class) // <1>
@EnableRedisHttpSession // <2>
public class Config {
@Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(); // <3>
}
@Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(); // <3>
}
}
// end::class[]

View File

@@ -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
* 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
* the License.
*/
package sample;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
@@ -36,34 +37,34 @@ import redis.embedded.RedisServer;
@Configuration
public class EmbeddedRedisConfiguration {
@Bean
public static RedisServerBean redisServer() {
return new RedisServerBean();
}
@Bean
public static RedisServerBean redisServer() {
return new RedisServerBean();
}
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void destroy() throws Exception {
if(redisServer != null) {
redisServer.stop();
}
}
public void destroy() throws Exception {
if(redisServer != null) {
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 {}
}
}

View File

@@ -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;
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
// tag::class[]
public class Initializer
extends AbstractHttpSessionApplicationInitializer { // <1>
extends AbstractHttpSessionApplicationInitializer { // <1>
public Initializer() {
super(Config.class); // <2>
}
public Initializer() {
super(Config.class); // <2>
}
}
// end::class[]

View File

@@ -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;
import javax.servlet.*;
@@ -5,16 +20,18 @@ import javax.servlet.annotation.*;
import javax.servlet.http.*;
import java.io.IOException;
// tag::class[]
@WebServlet("/session")
public class SessionServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String attributeName = req.getParameter("attributeName");
String attributeValue = req.getParameter("attributeValue");
req.getSession().setAttribute(attributeName, attributeValue);
resp.sendRedirect(req.getContextPath() + "/");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String attributeName = req.getParameter("attributeName");
String attributeValue = req.getParameter("attributeValue");
req.getSession().setAttribute(attributeName, attributeValue);
resp.sendRedirect(req.getContextPath() + "/");
}
private static final long serialVersionUID = 2878267318695777395L;
private static final long serialVersionUID = 2878267318695777395L;
}
// tag::end[]

View File

@@ -2,47 +2,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Session Attributes</title>
<link rel="stylesheet" href="assets/bootstrap.min.css">
<style type="text/css">
body {
padding: 1em;
}
</style>
<title>Session Attributes</title>
<link rel="stylesheet" href="assets/bootstrap.min.css">
<style type="text/css">
body {
padding: 1em;
}
</style>
</head>
<body>
<div class="container">
<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>
<div class="container">
<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>
<h1>Try it</h1>
<h1>Try it</h1>
<form class="form-inline" role="form" action="./session" method="post">
<label for="attributeValue">Attribute Name</label>
<input id="attributeValue" type="text" name="attributeName"/>
<label for="attributeValue">Attribute Value</label>
<input id="attributeValue" type="text" name="attributeValue"/>
<input type="submit" value="Set Attribute"/>
</form>
<form class="form-inline" role="form" action="./session" method="post">
<label for="attributeValue">Attribute Name</label>
<input id="attributeValue" type="text" name="attributeName"/>
<label for="attributeValue">Attribute Value</label>
<input id="attributeValue" type="text" name="attributeValue"/>
<input type="submit" value="Set Attribute"/>
</form>
<hr/>
<hr/>
<table class="table table-striped">
<thead>
<tr>
<th>Attribute Name</th>
<th>Attribute Value</th>
</tr>
</thead>
<tbody>
<c:forEach items="${sessionScope}" var="attr">
<tr>
<td><c:out value="${attr.key}"/></td>
<td><c:out value="${attr.value}"/></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>Attribute Name</th>
<th>Attribute Value</th>
</tr>
</thead>
<tbody>
<c:forEach items="${sessionScope}" var="attr">
<tr>
<td><c:out value="${attr.key}"/></td>
<td><c:out value="${attr.value}"/></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -3,22 +3,22 @@ apply from: TOMCAT_GRADLE
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarRunner {
skipProject = true
skipProject = true
}
dependencies {
compile project(':spring-session-data-redis'),
"org.springframework:spring-webmvc:$springVersion",
"redis.embedded:embedded-redis:0.2",
"org.springframework.security:spring-security-config:$springSecurityVersion",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"com.fasterxml.jackson.core:jackson-databind:$jacksonVersion",
jstlDependencies
compile project(':spring-session-data-redis'),
"org.springframework:spring-webmvc:$springVersion",
"redis.embedded:embedded-redis:0.2",
"org.springframework.security:spring-security-config:$springSecurityVersion",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"com.fasterxml.jackson.core:jackson-databind:$jacksonVersion",
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,
'org.codehaus.groovy.modules.http-builder:http-builder:0.7'
integrationTestCompile spockDependencies,
'org.codehaus.groovy.modules.http-builder:http-builder:0.7'
}

View File

@@ -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
import groovyx.net.http.HttpResponseException

View File

@@ -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
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
@@ -36,35 +36,33 @@ import redis.embedded.RedisServer;
@Configuration
public class EmbeddedRedisConfiguration {
@Bean
public static RedisServerBean redisServer() {
return new RedisServerBean();
}
@Bean
public static RedisServerBean redisServer() {
return new RedisServerBean();
}
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void destroy() throws Exception {
if(redisServer != null) {
redisServer.stop();
}
}
public void destroy() throws Exception {
if(redisServer != null) {
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 {}
}
}

View File

@@ -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;
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.HttpSessionStrategy;
// tag::class[]
@Configuration
@Import(EmbeddedRedisConfiguration.class) // <1>
@EnableRedisHttpSession // <2>
public class HttpSessionConfig {
@Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(); // <3>
}
@Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(); // <3>
}
@Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy(); // <4>
}
@Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy(); // <4>
}
}
// end::class[]

View File

@@ -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;
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
// tag::class[]
public class Initializer extends AbstractHttpSessionApplicationInitializer {
}
// end::class[]

View File

@@ -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
* 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
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}

View File

@@ -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
* 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
*/
public class SecurityInitializer extends
AbstractSecurityWebApplicationInitializer {
AbstractSecurityWebApplicationInitializer {
}

View File

@@ -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
* use this file except in compliance with the License. You may obtain a copy of

View File

@@ -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
* 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
*/
public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// tag::config[]
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SecurityConfig.class, HttpSessionConfig.class};
}
// end::config[]
// tag::config[]
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SecurityConfig.class, HttpSessionConfig.class};
}
// end::config[]
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { MvcConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { MvcConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}

View File

@@ -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
* use this file except in compliance with the License. You may obtain a copy of
@@ -15,33 +15,33 @@
*/
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.util.HashMap;
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
*/
@RestController
public class RestDemoController {
@RequestMapping(value="/",produces = "application/json")
public Map<String,String> helloUser(Principal principal) {
HashMap<String,String> result = new HashMap<String,String>();
result.put("username", principal.getName());
return result;
}
@RequestMapping(value="/",produces = "application/json")
public Map<String,String> helloUser(Principal principal) {
HashMap<String,String> result = new HashMap<String,String>();
result.put("username", principal.getName());
return result;
}
@RequestMapping("/logout")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void logout(HttpSession session) {
session.invalidate();
}
@RequestMapping("/logout")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void logout(HttpSession session) {
session.invalidate();
}
}

View File

@@ -3,20 +3,20 @@ apply from: TOMCAT_GRADLE
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarRunner {
skipProject = true
skipProject = true
}
dependencies {
compile project(':spring-session-data-redis'),
"org.springframework:spring-web:$springVersion",
"redis.embedded:embedded-redis:0.2",
"org.springframework.security:spring-security-config:$springSecurityVersion",
"org.springframework.security:spring-security-web:$springSecurityVersion",
jstlDependencies
compile project(':spring-session-data-redis'),
"org.springframework:spring-web:$springVersion",
"redis.embedded:embedded-redis:0.2",
"org.springframework.security:spring-security-config:$springSecurityVersion",
"org.springframework.security:spring-security-web:$springSecurityVersion",
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
}

View File

@@ -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
import geb.spock.GebReportingSpec
@@ -14,37 +29,37 @@ import spock.lang.Stepwise
@Stepwise
class SecurityTests extends GebReportingSpec {
def 'Unauthenticated user sent to log in page'() {
when: 'unauthenticated user request protected page'
via HomePage
then: 'sent to the log in page'
at LoginPage
}
def 'Unauthenticated user sent to log in page'() {
when: 'unauthenticated user request protected page'
via HomePage
then: 'sent to the log in page'
at LoginPage
}
def 'Log in views home page'() {
when: 'log in successfully'
login()
then: 'sent to original page'
at HomePage
and: 'the username is displayed'
username == 'user'
and: 'Spring Session Management is being used'
driver.manage().cookies.find { it.name == 'SESSION' }
and: 'Standard Session is NOT being used'
!driver.manage().cookies.find { it.name == 'JSESSIONID' }
}
def 'Log in views home page'() {
when: 'log in successfully'
login()
then: 'sent to original page'
at HomePage
and: 'the username is displayed'
username == 'user'
and: 'Spring Session Management is being used'
driver.manage().cookies.find { it.name == 'SESSION' }
and: 'Standard Session is NOT being used'
!driver.manage().cookies.find { it.name == 'JSESSIONID' }
}
def 'Log out success'() {
when:
logout()
then:
at LoginPage
}
def 'Log out success'() {
when:
logout()
then:
at LoginPage
}
def 'Logged out user sent to log in page'() {
when: 'logged out user request protected page'
via HomePage
then: 'sent to the log in page'
at LoginPage
}
def 'Logged out user sent to log in page'() {
when: 'logged out user request protected page'
via HomePage
then: 'sent to the log in page'
at LoginPage
}
}

View File

@@ -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
import geb.Page
@@ -8,10 +23,10 @@ import geb.Page
* @author Rob Winch
*/
class HomePage extends Page {
static url = ''
static at = { assert driver.title == 'Secured Content'; true}
static content = {
username { $('#un').text() }
logout(to:LoginPage) { $('input[type=submit]').click() }
}
static url = ''
static at = { assert driver.title == 'Secured Content'; true}
static content = {
username { $('#un').text() }
logout(to:LoginPage) { $('input[type=submit]').click() }
}
}

View File

@@ -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
import geb.Page
@@ -8,15 +23,15 @@ import geb.Page
* @author Rob Winch
*/
class LoginPage extends Page {
static url = '/login'
static at = { assert driver.title == 'Login Page'; true}
static content = {
form { $('form') }
submit { $('input[type=submit]') }
login(required:false) { user='user', pass='password' ->
form.username = user
form.password = pass
submit.click(HomePage)
}
}
static url = '/login'
static at = { assert driver.title == 'Login Page'; true}
static content = {
form { $('form') }
submit { $('input[type=submit]') }
login(required:false) { user='user', pass='password' ->
form.username = user
form.password = pass
submit.click(HomePage)
}
}
}

View File

@@ -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;
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.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
// tag::class[]
@Configuration
@Import(EmbeddedRedisConfiguration.class) // <1>
@EnableRedisHttpSession // <2>
public class Config {
@Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(); // <3>
}
@Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(); // <3>
}
}
// end::class[]

View File

@@ -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
* 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
* the License.
*/
package sample;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
@@ -36,35 +37,33 @@ import redis.embedded.RedisServer;
@Configuration
public class EmbeddedRedisConfiguration {
@Bean
public static RedisServerBean redisServer() {
return new RedisServerBean();
}
@Bean
public static RedisServerBean redisServer() {
return new RedisServerBean();
}
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void destroy() throws Exception {
if(redisServer != null) {
redisServer.stop();
}
}
public void destroy() throws Exception {
if(redisServer != null) {
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 {}
}
}

View File

@@ -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;
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
// tag::class[]
public class Initializer extends AbstractHttpSessionApplicationInitializer {
}
// end::class[]

View File

@@ -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
* 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
public class SecurityConfig {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}

View File

@@ -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;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
// tag::class[]
public class SecurityInitializer extends
AbstractSecurityWebApplicationInitializer {
AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityConfig.class, Config.class);
}
}
public SecurityInitializer() {
super(SecurityConfig.class, Config.class);
}
}
// end::class[]

View File

@@ -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;
import javax.servlet.ServletException;
@@ -10,13 +25,13 @@ import java.io.IOException;
@WebServlet("/session")
public class SessionServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String attributeName = req.getParameter("attributeName");
String attributeValue = req.getParameter("attributeValue");
req.getSession().setAttribute(attributeName, attributeValue);
resp.sendRedirect(req.getContextPath() + "/");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String attributeName = req.getParameter("attributeName");
String attributeValue = req.getParameter("attributeValue");
req.getSession().setAttribute(attributeName, attributeValue);
resp.sendRedirect(req.getContextPath() + "/");
}
private static final long serialVersionUID = 2878267318695777395L;
private static final long serialVersionUID = 2878267318695777395L;
}

View File

@@ -2,31 +2,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Secured Content</title>
<link rel="stylesheet" href="assets/bootstrap.min.css">
<style type="text/css">
body {
padding: 1em;
}
#un {
font-weight: bold;
}
</style>
<title>Secured Content</title>
<link rel="stylesheet" href="assets/bootstrap.min.css">
<style type="text/css">
body {
padding: 1em;
}
#un {
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<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>
<div class="container">
<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>
<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"/>
<form action="${logoutUrl}" method="post">
<input type="submit" value="Log Out"/>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
</div>
<c:url value="/logout" var="logoutUrl"/>
<form action="${logoutUrl}" method="post">
<input type="submit" value="Log Out"/>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
</div>
</body>
</html>

View File

@@ -3,18 +3,18 @@ apply from: TOMCAT_GRADLE
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarRunner {
skipProject = true
skipProject = true
}
dependencies {
compile project(':spring-session-data-redis'),
"org.springframework:spring-web:$springVersion",
"redis.embedded:embedded-redis:0.2",
jstlDependencies
compile project(':spring-session-data-redis'),
"org.springframework:spring-web:$springVersion",
"redis.embedded:embedded-redis:0.2",
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
}

View File

@@ -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
import geb.spock.*
@@ -13,112 +28,112 @@ import pages.*
*/
@Stepwise
class UserTests extends GebReportingSpec {
def 'first visit not authenticated'() {
when:
to HomePage
then:
form
!username
}
def 'first visit not authenticated'() {
when:
to HomePage
then:
form
!username
}
def 'invalid login'() {
setup:
def user = 'rob'
when:
login(user, user+'invalid')
then:
!username
error == 'Invalid username / password. Please ensure the username is the same as the password.'
}
def 'invalid login'() {
setup:
def user = 'rob'
when:
login(user, user+'invalid')
then:
!username
error == 'Invalid username / password. Please ensure the username is the same as the password.'
}
def 'empty username'() {
setup:
def user = ''
when:
login(user, user)
then:
!username
error == 'Invalid username / password. Please ensure the username is the same as the password.'
}
def 'empty username'() {
setup:
def user = ''
when:
login(user, user)
then:
!username
error == 'Invalid username / password. Please ensure the username is the same as the password.'
}
def 'login single user'() {
setup:
def user = 'rob'
when:
login(user, user)
then:
username == user
}
def 'login single user'() {
setup:
def user = 'rob'
when:
login(user, user)
then:
username == user
}
def 'add account'() {
when:
addAccount.click(HomePage)
then:
form
!username
}
def 'add account'() {
when:
addAccount.click(HomePage)
then:
form
!username
}
def 'log in second user'() {
setup:
def user = 'luke'
when:
login(user, user)
then:
username == user
}
def 'log in second user'() {
setup:
def user = 'luke'
when:
login(user, user)
then:
username == user
}
def 'following links keeps new session'() {
when:
navLink.click(LinkPage)
then:
username == 'luke'
}
def 'following links keeps new session'() {
when:
navLink.click(LinkPage)
then:
username == 'luke'
}
def 'switch account rob'() {
setup:
def user = 'rob'
when:
switchAccount(user)
then:
username == user
}
def 'switch account rob'() {
setup:
def user = 'rob'
when:
switchAccount(user)
then:
username == user
}
def 'following links keeps original session'() {
when:
navLink.click(LinkPage)
then:
username == 'rob'
}
def 'following links keeps original session'() {
when:
navLink.click(LinkPage)
then:
username == 'rob'
}
def 'switch account luke'() {
setup:
def user = 'luke'
when:
switchAccount(user)
then:
username == user
}
def 'switch account luke'() {
setup:
def user = 'luke'
when:
switchAccount(user)
then:
username == user
}
def 'logout luke'() {
when:
logout.click(HomePage)
then:
!username
}
def 'logout luke'() {
when:
logout.click(HomePage)
then:
!username
}
def 'switch back rob'() {
setup:
def user = 'rob'
when:
switchAccount(user)
then:
username == user
}
def 'switch back rob'() {
setup:
def user = 'rob'
when:
switchAccount(user)
then:
username == user
}
def 'logout rob'() {
when:
logout.click(HomePage)
then:
!username
}
def 'logout rob'() {
when:
logout.click(HomePage)
then:
!username
}
}

View File

@@ -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
import geb.*
@@ -8,24 +23,24 @@ import geb.*
* @author Rob Winch
*/
class HomePage extends Page {
static url = ''
static at = { assert driver.title == 'Demonstrates Multi User Log In'; true}
static content = {
navLink { $('#navLink') }
error { $('#error').text() }
form { $('form') }
username(required:false) { $('#un').text() }
logout(required:false) { $('#logout') }
addAccount(required:false) { $('#addAccount') }
submit { $('input[type=submit]') }
login(required:false) { user, pass ->
form.username = user
form.password = pass
submit.click(HomePage)
}
switchAccount{ un ->
$("#switchAccount${un}").click(HomePage)
}
attributes { moduleList AttributeRow, $("table tr").tail() }
}
static url = ''
static at = { assert driver.title == 'Demonstrates Multi User Log In'; true}
static content = {
navLink { $('#navLink') }
error { $('#error').text() }
form { $('form') }
username(required:false) { $('#un').text() }
logout(required:false) { $('#logout') }
addAccount(required:false) { $('#addAccount') }
submit { $('input[type=submit]') }
login(required:false) { user, pass ->
form.username = user
form.password = pass
submit.click(HomePage)
}
switchAccount{ un ->
$("#switchAccount${un}").click(HomePage)
}
attributes { moduleList AttributeRow, $("table tr").tail() }
}
}

View File

@@ -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
import geb.*
@@ -8,13 +23,13 @@ import geb.*
* @author Rob Winch
*/
class LinkPage extends Page {
static url = ''
static at = { assert driver.title == 'Linked Page'; true}
static content = {
form { $('#navLinks') }
username(required:false) { $('#un').text() }
switchAccount{ un ->
$("#switchAccount${un}").click(HomePage)
}
}
static url = ''
static at = { assert driver.title == 'Linked Page'; true}
static content = {
form { $('#navLinks') }
username(required:false) { $('#un').text() }
switchAccount{ un ->
$("#switchAccount${un}").click(HomePage)
}
}
}

View File

@@ -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 use this file except in compliance with the License.
@@ -16,29 +16,29 @@
package sample;
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) {
super();
this.username = username;
this.logoutUrl = logoutUrl;
this.switchAccountUrl = switchAccountUrl;
}
public Account(String username, String logoutUrl, String switchAccountUrl) {
super();
this.username = username;
this.logoutUrl = logoutUrl;
this.switchAccountUrl = switchAccountUrl;
}
public String getUsername() {
return username;
}
public String getUsername() {
return username;
}
public String getLogoutUrl() {
return logoutUrl;
}
public String getLogoutUrl() {
return logoutUrl;
}
public String getSwitchAccountUrl() {
return switchAccountUrl;
}
public String getSwitchAccountUrl() {
return switchAccountUrl;
}
}

View File

@@ -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
* 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
* the License.
*/
package sample;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -24,13 +24,15 @@ import org.springframework.session.data.redis.config.annotation.web.http.EnableR
/**
* @author Rob Winch
*/
// tag::class[]
@Import(EmbeddedRedisConfiguration.class)
@Configuration
@EnableRedisHttpSession
public class Config {
@Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
@Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
}
// end::class[]

View File

@@ -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
* 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
* the License.
*/
package sample;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
@@ -36,36 +37,34 @@ import redis.embedded.RedisServer;
@Configuration
public class EmbeddedRedisConfiguration {
@Bean
public RedisServerBean redisServer() {
return new RedisServerBean();
}
@Bean
public RedisServerBean redisServer() {
return new RedisServerBean();
}
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void destroy() throws Exception {
if(redisServer != null) {
redisServer.stop();
}
}
public void destroy() throws Exception {
if(redisServer != null) {
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 {}
}
}

View File

@@ -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
* 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
* the License.
*/
package sample;
import javax.servlet.ServletContext;
@@ -25,12 +24,12 @@ import org.springframework.session.web.context.AbstractHttpSessionApplicationIni
*/
public class Initializer extends AbstractHttpSessionApplicationInitializer {
public Initializer() {
super(Config.class);
}
public Initializer() {
super(Config.class);
}
@Override
protected void afterSessionRepositoryFilter(ServletContext servletContext) {
appendFilters(servletContext, new UserAccountsFilter());
}
@Override
protected void afterSessionRepositoryFilter(ServletContext servletContext) {
appendFilters(servletContext, new UserAccountsFilter());
}
}

View File

@@ -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 use this file except in compliance with the License.
@@ -26,21 +26,21 @@ import javax.servlet.http.HttpServletResponse;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
if(username != null && !"".equals(username) && username.equals(password)) {
req.getSession().setAttribute("username", username);
String url = resp.encodeRedirectURL(req.getContextPath() + "/");
resp.sendRedirect(url);
} else {
String url = resp.encodeRedirectURL(req.getContextPath() + "/?error");
resp.sendRedirect(url);
}
}
if(username != null && !"".equals(username) && username.equals(password)) {
req.getSession().setAttribute("username", username);
String url = resp.encodeRedirectURL(req.getContextPath() + "/");
resp.sendRedirect(url);
} else {
String url = resp.encodeRedirectURL(req.getContextPath() + "/?error");
resp.sendRedirect(url);
}
}
private static final long serialVersionUID = -8157634860354132501L;
private static final long serialVersionUID = -8157634860354132501L;
}

View File

@@ -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 use this file except in compliance with the License.
@@ -27,16 +27,16 @@ import javax.servlet.http.HttpSession;
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
HttpSession session = req.getSession(false);
if(session != null) {
session.invalidate();
}
String url = resp.encodeRedirectURL(req.getContextPath() + "/");
resp.sendRedirect(url);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
HttpSession session = req.getSession(false);
if(session != null) {
session.invalidate();
}
String url = resp.encodeRedirectURL(req.getContextPath() + "/");
resp.sendRedirect(url);
}
private static final long serialVersionUID = 4061762524521437433L;
private static final long serialVersionUID = 4061762524521437433L;
}

View File

@@ -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 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 void init(FilterConfig filterConfig) throws ServletException {
}
public void init(FilterConfig filterConfig) throws ServletException {
}
@SuppressWarnings("unchecked")
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
@SuppressWarnings("unchecked")
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// tag::HttpSessionManager[]
HttpSessionManager sessionManager =
(HttpSessionManager) httpRequest.getAttribute(HttpSessionManager.class.getName());
// end::HttpSessionManager[]
SessionRepository<Session> repo =
(SessionRepository<Session>) httpRequest.getAttribute(SessionRepository.class.getName());
// tag::HttpSessionManager[]
HttpSessionManager sessionManager =
(HttpSessionManager) httpRequest.getAttribute(HttpSessionManager.class.getName());
// end::HttpSessionManager[]
SessionRepository<Session> repo =
(SessionRepository<Session>) httpRequest.getAttribute(SessionRepository.class.getName());
String currentSessionAlias = sessionManager.getCurrentSessionAlias(httpRequest);
Map<String, String> sessionIds = sessionManager.getSessionIds(httpRequest);
String unauthenticatedAlias = null;
String currentSessionAlias = sessionManager.getCurrentSessionAlias(httpRequest);
Map<String, String> sessionIds = sessionManager.getSessionIds(httpRequest);
String unauthenticatedAlias = null;
String contextPath = httpRequest.getContextPath();
List<Account> accounts = new ArrayList<Account>();
Account currentAccount = null;
for(Map.Entry<String, String> entry : sessionIds.entrySet()) {
String alias = entry.getKey();
String sessionId = entry.getValue();
String contextPath = httpRequest.getContextPath();
List<Account> accounts = new ArrayList<Account>();
Account currentAccount = null;
for(Map.Entry<String, String> entry : sessionIds.entrySet()) {
String alias = entry.getKey();
String sessionId = entry.getValue();
Session session = repo.getSession(sessionId);
if(session == null) {
continue;
}
Session session = repo.getSession(sessionId);
if(session == null) {
continue;
}
String username = session.getAttribute("username");
if(username == null) {
unauthenticatedAlias = alias;
continue;
}
String username = session.getAttribute("username");
if(username == null) {
unauthenticatedAlias = alias;
continue;
}
String logoutUrl = sessionManager.encodeURL("./logout", alias);
String switchAccountUrl = sessionManager.encodeURL("./", alias);
Account account = new Account(username, logoutUrl, switchAccountUrl);
if(currentSessionAlias.equals(alias)) {
currentAccount = account;
} else {
accounts.add(account);
}
}
String logoutUrl = sessionManager.encodeURL("./logout", alias);
String switchAccountUrl = sessionManager.encodeURL("./", alias);
Account account = new Account(username, logoutUrl, switchAccountUrl);
if(currentSessionAlias.equals(alias)) {
currentAccount = account;
} else {
accounts.add(account);
}
}
// tag::addAccountUrl[]
String addAlias = unauthenticatedAlias == null ? // <1>
sessionManager.getNewSessionAlias(httpRequest) : // <2>
unauthenticatedAlias; // <3>
String addAccountUrl = sessionManager.encodeURL(contextPath, addAlias); // <4>
// end::addAccountUrl[]
// tag::addAccountUrl[]
String addAlias = unauthenticatedAlias == null ? // <1>
sessionManager.getNewSessionAlias(httpRequest) : // <2>
unauthenticatedAlias; // <3>
String addAccountUrl = sessionManager.encodeURL(contextPath, addAlias); // <4>
// end::addAccountUrl[]
httpRequest.setAttribute("currentAccount", currentAccount);
httpRequest.setAttribute("addAccountUrl", addAccountUrl);
httpRequest.setAttribute("accounts", accounts);
httpRequest.setAttribute("currentAccount", currentAccount);
httpRequest.setAttribute("addAccountUrl", addAccountUrl);
httpRequest.setAttribute("accounts", accounts);
chain.doFilter(request, response);
}
chain.doFilter(request, response);
}
public void destroy() {
}
public void destroy() {
}
}

View File

@@ -1,10 +1,10 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
}
}
apply plugin: 'spring-boot'
@@ -17,23 +17,23 @@ tasks.findByPath("artifactoryPublish")?.enabled = false
group = 'samples'
dependencies {
compile project(':spring-session-data-redis'),
"org.springframework.boot:spring-boot-starter-web",
"org.springframework.boot:spring-boot-starter-data-jpa",
"org.springframework.boot:spring-boot-starter-thymeleaf",
"org.springframework.boot:spring-boot-starter-websocket",
"org.springframework:spring-websocket:${springVersion}",
"org.springframework.data:spring-data-jpa:1.7.0.RELEASE",
"redis.embedded:embedded-redis:0.2",
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
"com.h2database:h2",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"org.springframework.security:spring-security-config:$springSecurityVersion",
"org.springframework.security:spring-security-messaging:$springSecurityVersion",
"org.springframework.security:spring-security-data:$springSecurityVersion"
compile project(':spring-session-data-redis'),
"org.springframework.boot:spring-boot-starter-web",
"org.springframework.boot:spring-boot-starter-data-jpa",
"org.springframework.boot:spring-boot-starter-thymeleaf",
"org.springframework.boot:spring-boot-starter-websocket",
"org.springframework:spring-websocket:${springVersion}",
"org.springframework.data:spring-data-jpa:1.7.0.RELEASE",
"redis.embedded:embedded-redis:0.2",
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
"com.h2database:h2",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"org.springframework.security:spring-security-config:$springSecurityVersion",
"org.springframework.security:spring-security-messaging:$springSecurityVersion",
"org.springframework.security:spring-security-data:$springSecurityVersion"
testCompile "org.springframework.boot:spring-boot-starter-test",
"org.springframework.security:spring-security-test:$springSecurityVersion"
testCompile "org.springframework.boot:spring-boot-starter-test",
"org.springframework.security:spring-security-test:$springSecurityVersion"
}

View File

@@ -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
* 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
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -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;
import javax.sql.DataSource;
@@ -11,14 +26,14 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}
@Bean
public JedisConnectionFactory connectionFactory() throws Exception {
return new JedisConnectionFactory();
}
@Bean
public JedisConnectionFactory connectionFactory() throws Exception {
return new JedisConnectionFactory();
}
}

View File

@@ -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
* use this file except in compliance with the License. You may obtain a copy of
@@ -33,36 +33,34 @@ import redis.embedded.RedisServer;
@Configuration
public class EmbeddedRedisConfig {
@Bean
public static RedisServerBean redisServer() {
return new RedisServerBean();
}
@Bean
public static RedisServerBean redisServer() {
return new RedisServerBean();
}
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void destroy() throws Exception {
if(redisServer != null) {
redisServer.stop();
}
}
public void destroy() throws Exception {
if(redisServer != null) {
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 {}
}
}

View File

@@ -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 use this file except in compliance with the License.
@@ -32,7 +32,7 @@ public class H2Initializer {
@Bean
public ServletRegistrationBean h2Servlet() {
ServletRegistrationBean servletBean = new ServletRegistrationBean();
servletBean.addUrlMappings("/h2/*");
servletBean.addUrlMappings("/h2/*");
servletBean.setServlet(new WebServlet());
return servletBean;
}

View File

@@ -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 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[]
@EnableRedisHttpSession//(maxInactiveIntervalInSeconds = 60)
public class WebSecurityConfig
extends WebSecurityConfigurerAdapter {
extends WebSecurityConfigurerAdapter {
// end::enable-redis-httpsession[]
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.logout()
.permitAll();
}
// @formatter:on
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.logout()
.permitAll();
}
// @formatter:off
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsService userDetailsService) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
// @formatter:on
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsService userDetailsService) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
}

View File

@@ -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
* use this file except in compliance with the License. You may obtain a copy of
@@ -15,7 +15,6 @@
*/
package sample.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -29,16 +28,16 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@EnableScheduling
@EnableWebSocketMessageBroker
public class WebSocketConfig
extends AbstractSessionWebSocketMessageBrokerConfigurer<ExpiringSession> { // <1>
extends AbstractSessionWebSocketMessageBrokerConfigurer<ExpiringSession> { // <1>
protected void configureStompEndpoints(StompEndpointRegistry registry) { // <2>
registry.addEndpoint("/messages")
.withSockJS();
}
protected void configureStompEndpoints(StompEndpointRegistry registry) { // <2>
registry.addEndpoint("/messages")
.withSockJS();
}
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
}
// end::class[]

View File

@@ -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
* use this file except in compliance with the License. You may obtain a copy of
@@ -31,13 +31,13 @@ import sample.websocket.WebSocketDisconnectHandler;
@Configuration
public class WebSocketHandlersConfig<S extends ExpiringSession> {
@Bean
public WebSocketConnectHandler<S> webSocketConnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
return new WebSocketConnectHandler<S>(messagingTemplate, repository);
}
@Bean
public WebSocketConnectHandler<S> webSocketConnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
return new WebSocketConnectHandler<S>(messagingTemplate, repository);
}
@Bean
public WebSocketDisconnectHandler<S> webSocketDisconnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
return new WebSocketDisconnectHandler<S>(messagingTemplate, repository);
}
@Bean
public WebSocketDisconnectHandler<S> webSocketDisconnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
return new WebSocketDisconnectHandler<S>(messagingTemplate, repository);
}
}

View File

@@ -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
* 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
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.antMatchers(SimpMessageType.MESSAGE,"/queue/**","/topic/**").denyAll()
.antMatchers(SimpMessageType.SUBSCRIBE, "/queue/**/*-user*","/topic/**/*-user*").denyAll()
.anyMessage().authenticated();
}
// @formatter:off
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.antMatchers(SimpMessageType.MESSAGE,"/queue/**","/topic/**").denyAll()
.antMatchers(SimpMessageType.SUBSCRIBE, "/queue/**/*-user*","/topic/**/*-user*").denyAll()
.anyMessage().authenticated();
}
// @formatter:on
}

View File

@@ -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;
import java.util.Calendar;
import javax.persistence.Entity;
@@ -10,13 +24,13 @@ import javax.persistence.Id;
public class ActiveWebSocketUser {
@Id
private String id;
private String username;
private Calendar connectionTime;
public ActiveWebSocketUser() {}
public ActiveWebSocketUser(String id, String username, Calendar connectionTime) {
super();
this.id = id;
@@ -39,6 +53,6 @@ public class ActiveWebSocketUser {
public void setConnectionTime(Calendar connectionTime) {
this.connectionTime = connectionTime;
}
}

View File

@@ -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;
import java.util.List;

View File

@@ -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;
import java.util.Calendar;
public class InstantMessage {
private String to;
private String from;
private String message;
private Calendar created = Calendar.getInstance();
public String getTo() {
@@ -42,7 +57,7 @@ public class InstantMessage {
public void setCreated(Calendar created) {
this.created = created;
}
}

View File

@@ -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 use this file except in compliance with the License.
@@ -40,73 +40,73 @@ import org.springframework.security.crypto.password.PasswordEncoder;
@Entity
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotEmpty(message = "First name is required.")
private String firstName;
@NotEmpty(message = "First name is required.")
private String firstName;
@NotEmpty(message = "Last name is required.")
private String lastName;
@NotEmpty(message = "Last name is required.")
private String lastName;
@Email(message = "Please provide a valid email address.")
@NotEmpty(message = "Email is required.")
@Column(unique=true, nullable = false)
private String email;
@Email(message = "Please provide a valid email address.")
@NotEmpty(message = "Email is required.")
@Column(unique=true, nullable = false)
private String email;
@NotEmpty(message = "Password is required.")
private String password;
@NotEmpty(message = "Password is required.")
private String password;
public User() {}
public User() {}
public User(User user) {
this.id = user.id;
this.firstName = user.firstName;
this.lastName = user.lastName;
this.email = user.email;
this.password = user.password;
}
public User(User user) {
this.id = user.id;
this.firstName = user.firstName;
this.lastName = user.lastName;
this.email = user.email;
this.password = user.password;
}
public String getPassword() {
return password;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void setPassword(String password) {
this.password = password;
}
public Long getId() {
return id;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public void setEmail(String email) {
this.email = email;
}
private static final long serialVersionUID = 2738859149330833739L;
private static final long serialVersionUID = 2738859149330833739L;
}

View File

@@ -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
* 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> {
User findByEmail(String email);
User findByEmail(String email);
}

View File

@@ -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
* 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.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -40,29 +39,29 @@ import sample.security.CurrentUser;
@Controller
@RequestMapping("/")
public class MessageController {
private SimpMessageSendingOperations messagingTemplate;
private ActiveWebSocketUserRepository activeUserRepository;
private SimpMessageSendingOperations messagingTemplate;
private ActiveWebSocketUserRepository activeUserRepository;
@Autowired
public MessageController(ActiveWebSocketUserRepository activeUserRepository,SimpMessageSendingOperations messagingTemplate) {
this.activeUserRepository = activeUserRepository;
this.messagingTemplate = messagingTemplate;
}
@Autowired
public MessageController(ActiveWebSocketUserRepository activeUserRepository,SimpMessageSendingOperations messagingTemplate) {
this.activeUserRepository = activeUserRepository;
this.messagingTemplate = messagingTemplate;
}
@RequestMapping("/")
public String im() {
return "index";
}
@RequestMapping("/")
public String im() {
return "index";
}
@MessageMapping("/im")
public void im(InstantMessage im, @CurrentUser User currentUser) {
im.setFrom(currentUser.getEmail());
messagingTemplate.convertAndSendToUser(im.getTo(),"/queue/messages",im);
messagingTemplate.convertAndSendToUser(im.getFrom(),"/queue/messages",im);
}
@MessageMapping("/im")
public void im(InstantMessage im, @CurrentUser User currentUser) {
im.setFrom(currentUser.getEmail());
messagingTemplate.convertAndSendToUser(im.getTo(),"/queue/messages",im);
messagingTemplate.convertAndSendToUser(im.getFrom(),"/queue/messages",im);
}
@SubscribeMapping("/users")
public List<String> subscribeMessages() throws Exception {
return activeUserRepository.findAllActiveUsers();
}
@SubscribeMapping("/users")
public List<String> subscribeMessages() throws Exception {
return activeUserRepository.findAllActiveUsers();
}
}

View File

@@ -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 use this file except in compliance with the License.

View File

@@ -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 use this file except in compliance with the License.
@@ -33,55 +33,55 @@ import org.springframework.stereotype.Service;
*/
@Service
public class UserRepositoryUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
private final UserRepository userRepository;
@Autowired
public UserRepositoryUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Autowired
public UserRepositoryUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
/* (non-Javadoc)
* @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
*/
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByEmail(username);
if(user == null) {
throw new UsernameNotFoundException("Could not find user " + username);
}
return new CustomUserDetails(user);
}
/* (non-Javadoc)
* @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
*/
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByEmail(username);
if(user == null) {
throw new UsernameNotFoundException("Could not find user " + username);
}
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) {
super(user);
}
private CustomUserDetails(User user) {
super(user);
}
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.createAuthorityList("ROLE_USER");
}
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.createAuthorityList("ROLE_USER");
}
public String getUsername() {
return getEmail();
}
public String getUsername() {
return getEmail();
}
public boolean isAccountNonExpired() {
return true;
}
public boolean isAccountNonExpired() {
return true;
}
public boolean isAccountNonLocked() {
return true;
}
public boolean isAccountNonLocked() {
return true;
}
public boolean isCredentialsNonExpired() {
return true;
}
public boolean isCredentialsNonExpired() {
return true;
}
public boolean isEnabled() {
return true;
}
public boolean isEnabled() {
return true;
}
private static final long serialVersionUID = 5639683223516504866L;
}
private static final long serialVersionUID = 5639683223516504866L;
}
}

View File

@@ -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 use this file except in compliance with the License.
@@ -29,23 +29,23 @@ import sample.data.ActiveWebSocketUser;
import sample.data.ActiveWebSocketUserRepository;
public class WebSocketConnectHandler<S> implements ApplicationListener<SessionConnectEvent> {
private ActiveWebSocketUserRepository repository;
private SimpMessageSendingOperations messagingTemplate;
private ActiveWebSocketUserRepository repository;
private SimpMessageSendingOperations messagingTemplate;
public WebSocketConnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
super();
this.messagingTemplate = messagingTemplate;
this.repository = repository;
}
public WebSocketConnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
super();
this.messagingTemplate = messagingTemplate;
this.repository = repository;
}
public void onApplicationEvent(SessionConnectEvent event) {
MessageHeaders headers = event.getMessage().getHeaders();
Principal user = SimpMessageHeaderAccessor.getUser(headers);
if(user == null) {
return;
}
String id = SimpMessageHeaderAccessor.getSessionId(headers);
repository.save(new ActiveWebSocketUser(id, user.getName(), Calendar.getInstance()));
messagingTemplate.convertAndSend("/topic/friends/signin", Arrays.asList(user.getName()));
}
public void onApplicationEvent(SessionConnectEvent event) {
MessageHeaders headers = event.getMessage().getHeaders();
Principal user = SimpMessageHeaderAccessor.getUser(headers);
if(user == null) {
return;
}
String id = SimpMessageHeaderAccessor.getSessionId(headers);
repository.save(new ActiveWebSocketUser(id, user.getName(), Calendar.getInstance()));
messagingTemplate.convertAndSend("/topic/friends/signin", Arrays.asList(user.getName()));
}
}

View File

@@ -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 use this file except in compliance with the License.
@@ -25,27 +25,27 @@ import sample.data.ActiveWebSocketUser;
import sample.data.ActiveWebSocketUserRepository;
public class WebSocketDisconnectHandler<S> implements ApplicationListener<SessionDisconnectEvent> {
private ActiveWebSocketUserRepository repository;
private SimpMessageSendingOperations messagingTemplate;
private ActiveWebSocketUserRepository repository;
private SimpMessageSendingOperations messagingTemplate;
public WebSocketDisconnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
super();
this.messagingTemplate = messagingTemplate;
this.repository = repository;
}
public WebSocketDisconnectHandler(SimpMessageSendingOperations messagingTemplate, ActiveWebSocketUserRepository repository) {
super();
this.messagingTemplate = messagingTemplate;
this.repository = repository;
}
public void onApplicationEvent(SessionDisconnectEvent event) {
String id = event.getSessionId();
if(id == null) {
return;
}
ActiveWebSocketUser user = repository.findOne(id);
if(user == null) {
return;
}
public void onApplicationEvent(SessionDisconnectEvent event) {
String id = event.getSessionId();
if(id == null) {
return;
}
ActiveWebSocketUser user = repository.findOne(id);
if(user == null) {
return;
}
repository.delete(id);
messagingTemplate.convertAndSend("/topic/friends/signout", Arrays.asList(user.getUsername()));
repository.delete(id);
messagingTemplate.convertAndSend("/topic/friends/signout", Arrays.asList(user.getUsername()));
}
}
}

View File

@@ -1,139 +1,139 @@
function ApplicationModel(stompClient) {
var self = this;
var self = this;
self.friends = ko.observableArray();
self.username = ko.observable();
self.conversation = ko.observable(new ImConversationModel(stompClient,this.username));
self.notifications = ko.observableArray();
self.friends = ko.observableArray();
self.username = ko.observable();
self.conversation = ko.observable(new ImConversationModel(stompClient,this.username));
self.notifications = ko.observableArray();
self.connect = function() {
stompClient.connect({}, function(frame) {
self.connect = function() {
stompClient.connect({}, function(frame) {
console.log('Connected ' + frame);
self.username(frame.headers['user-name']);
console.log('Connected ' + frame);
self.username(frame.headers['user-name']);
// self.friendSignin({"username": "luke"});
stompClient.subscribe("/user/queue/errors", function(message) {
self.pushNotification("Error " + message.body);
});
stompClient.subscribe("/app/users", function(message) {
var friends = JSON.parse(message.body);
stompClient.subscribe("/user/queue/errors", function(message) {
self.pushNotification("Error " + message.body);
});
stompClient.subscribe("/app/users", function(message) {
var friends = JSON.parse(message.body);
for(var i=0;i<friends.length;i++) {
self.friendSignin({"username": friends[i]});
}
});
stompClient.subscribe("/topic/friends/signin", function(message) {
var friends = JSON.parse(message.body);
for(var i=0;i<friends.length;i++) {
self.friendSignin({"username": friends[i]});
}
});
stompClient.subscribe("/topic/friends/signin", function(message) {
var friends = JSON.parse(message.body);
for(var i=0;i<friends.length;i++) {
self.friendSignin(new ImFriend({"username": friends[i]}));
}
});
stompClient.subscribe("/topic/friends/signout", function(message) {
var friends = JSON.parse(message.body);
for(var i=0;i<friends.length;i++) {
self.friendSignin(new ImFriend({"username": friends[i]}));
}
});
stompClient.subscribe("/topic/friends/signout", function(message) {
var friends = JSON.parse(message.body);
for(var i=0;i<friends.length;i++) {
self.friendSignout(new ImFriend({"username": friends[i]}));
}
});
stompClient.subscribe("/user/queue/messages", function(message) {
self.conversation().receiveMessage(JSON.parse(message.body));
});
}, function(error) {
self.pushNotification(error)
console.log("STOMP protocol error " + error);
});
}
for(var i=0;i<friends.length;i++) {
self.friendSignout(new ImFriend({"username": friends[i]}));
}
});
stompClient.subscribe("/user/queue/messages", function(message) {
self.conversation().receiveMessage(JSON.parse(message.body));
});
}, function(error) {
self.pushNotification(error)
console.log("STOMP protocol error " + error);
});
}
self.pushNotification = function(text) {
self.notifications.push({notification: text});
if (self.notifications().length > 5) {
self.notifications.shift();
}
}
self.pushNotification = function(text) {
self.notifications.push({notification: text});
if (self.notifications().length > 5) {
self.notifications.shift();
}
}
self.logout = function() {
stompClient.disconnect();
window.location.href = "../logout.html";
}
self.logout = function() {
stompClient.disconnect();
window.location.href = "../logout.html";
}
self.friendSignin = function(friend) {
self.friends.push(friend);
}
self.friendSignin = function(friend) {
self.friends.push(friend);
}
self.friendSignout = function(friend) {
var r = self.friends.remove(
function(item) {
item.username == friend.username
}
);
self.friends(r);
}
self.friendSignout = function(friend) {
var r = self.friends.remove(
function(item) {
item.username == friend.username
}
);
self.friends(r);
}
}
function ImFriend(data) {
var self = this;
var self = this;
self.username = data.username;
self.username = data.username;
}
function ImConversationModel(stompClient,from) {
var self = this;
self.stompClient = stompClient;
self.from = from;
self.to = ko.observable(new ImFriend('null'));
self.draft = ko.observable('')
var self = this;
self.stompClient = stompClient;
self.from = from;
self.to = ko.observable(new ImFriend('null'));
self.draft = ko.observable('')
self.messages = ko.observableArray();
self.messages = ko.observableArray();
self.receiveMessage = function(message) {
var elem = $('#chat');
var isFromSelf = self.from() == message.from;
var isFromTo = self.to().username == message.from;
if(!(isFromTo || isFromSelf)) {
self.chat(new ImFriend({"username":message.from}))
}
self.receiveMessage = function(message) {
var elem = $('#chat');
var isFromSelf = self.from() == message.from;
var isFromTo = self.to().username == message.from;
if(!(isFromTo || isFromSelf)) {
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)
elem.scrollTop(elem[0].scrollHeight);
};
if (atBottom)
elem.scrollTop(elem[0].scrollHeight);
};
self.chat = function(to) {
self.to(to);
self.draft('');
self.messages.removeAll()
$('#trade-dialog').modal();
}
self.chat = function(to) {
self.to(to);
self.draft('');
self.messages.removeAll()
$('#trade-dialog').modal();
}
self.send = function() {
var data = {
"created" : new Date(),
"from" : self.from(),
"to" : self.to().username,
"message" : self.draft()
};
var destination = "/app/im"; // /queue/messages-user1
stompClient.send(destination, {}, JSON.stringify(data));
self.draft('');
}
self.send = function() {
var data = {
"created" : new Date(),
"from" : self.from(),
"to" : self.to().username,
"message" : self.draft()
};
var destination = "/app/im"; // /queue/messages-user1
stompClient.send(destination, {}, JSON.stringify(data));
self.draft('');
}
};
function ImModel(data) {
var self = this;
var self = this;
self.created = new Date(data.created);
self.to = data.to;
self.message = data.message;
self.from = data.from;
self.messageFormatted = ko.computed(function() {
return self.created.getHours() + ":" + self.created.getMinutes() + ":" + self.created.getSeconds() + " - " + self.from + " - " + self.message;
})
self.created = new Date(data.created);
self.to = data.to;
self.message = data.message;
self.from = data.from;
self.messageFormatted = ko.computed(function() {
return self.created.getHours() + ":" + self.created.getMinutes() + ":" + self.created.getSeconds() + " - " + self.from + " - " + self.message;
})
};

View File

@@ -1,74 +1,74 @@
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="layout">
<head>
<title>View All</title>
</head>
<body>
<div layout:fragment="content">
<div class="container">
<div id="heading" class="masthead">
<h3 class="muted">Chat Application</h3>
</div>
<div id="main-content">
<table class="table table-striped">
<thead>
<tr>
<th>User</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: friends()">
<tr>
<td data-bind="text: username"></td>
<td class="trade-buttons">
<button class="btn btn-primary" data-bind="click: $root.conversation().chat">Chat</button>
</td>
</tr>
</tbody>
</table>
<head>
<title>View All</title>
</head>
<body>
<div layout:fragment="content">
<div class="container">
<div id="heading" class="masthead">
<h3 class="muted">Chat Application</h3>
</div>
<div id="main-content">
<table class="table table-striped">
<thead>
<tr>
<th>User</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: friends()">
<tr>
<td data-bind="text: username"></td>
<td class="trade-buttons">
<button class="btn btn-primary" data-bind="click: $root.conversation().chat">Chat</button>
</td>
</tr>
</tbody>
</table>
<div id="trade-dialog" class="modal hide fade" tabindex="-1">
<div class="modal-body">
<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 data-bind="text: messageFormatted"></div>
</div>
<form class="form-horizontal" data-bind="submit: conversation().send">
<textarea data-bind="value: conversation().draft"></textarea>
<button class="btn btn-primary" type="submit">Send</button>
</form>
</div>
</div>
<div class="alert alert-warning">
<h5>Notifications</h5>
<ul data-bind="foreach: notifications">
<li data-bind="text: notification"></li>
</ul>
</div>
</div>
</div>
<div id="trade-dialog" class="modal hide fade" tabindex="-1">
<div class="modal-body">
<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 data-bind="text: messageFormatted"></div>
</div>
<form class="form-horizontal" data-bind="submit: conversation().send">
<textarea data-bind="value: conversation().draft"></textarea>
<button class="btn btn-primary" type="submit">Send</button>
</form>
</div>
</div>
<div class="alert alert-warning">
<h5>Notifications</h5>
<ul data-bind="foreach: notifications">
<li data-bind="text: notification"></li>
</ul>
</div>
</div>
</div>
<!-- 3rd party -->
<script src="resources/js/jquery/jquery.js"></script>
<script src="resources/js/bootstrap/js/bootstrap.js"></script>
<script src="resources/js/knockout/knockout.js"></script>
<script src="resources/js/sockjs/sockjs.js"></script>
<script src="resources/js/stomp/lib/stomp.js"></script>
<!-- 3rd party -->
<script src="resources/js/jquery/jquery.js"></script>
<script src="resources/js/bootstrap/js/bootstrap.js"></script>
<script src="resources/js/knockout/knockout.js"></script>
<script src="resources/js/sockjs/sockjs.js"></script>
<script src="resources/js/stomp/lib/stomp.js"></script>
<!-- application -->
<script src="resources/js/message.js"></script>
<script type="text/javascript">
(function() {
var socket = new SockJS('/messages');
var stompClient = Stomp.over(socket);
<!-- application -->
<script src="resources/js/message.js"></script>
<script type="text/javascript">
(function() {
var socket = new SockJS('/messages');
var stompClient = Stomp.over(socket);
var appModel = new ApplicationModel(stompClient);
ko.applyBindings(appModel);
var appModel = new ApplicationModel(stompClient);
ko.applyBindings(appModel);
appModel.connect();
appModel.connect();
})();
</script>
</div>
</body>
})();
</script>
</div>
</body>
</html>

View File

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

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