Compare commits
29 Commits
1.2.1.RELE
...
1.3.0.M1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
997813088a | ||
|
|
5ecf390932 | ||
|
|
8167b43e63 | ||
|
|
1f7193f32d | ||
|
|
0e1d81f509 | ||
|
|
8b97a32db2 | ||
|
|
d3379029bb | ||
|
|
26eca5b448 | ||
|
|
6335894e13 | ||
|
|
34948d6451 | ||
|
|
bff0f8f845 | ||
|
|
2052ec8d44 | ||
|
|
cbd96999e0 | ||
|
|
61492c4ae1 | ||
|
|
97fef0f9bd | ||
|
|
2792d2a0e9 | ||
|
|
2724b333b3 | ||
|
|
8db7d394ba | ||
|
|
3e293e8b54 | ||
|
|
de7bb05fc1 | ||
|
|
3ee4c5b5d0 | ||
|
|
acf37fc8f4 | ||
|
|
1256a94d7e | ||
|
|
bbb94361f8 | ||
|
|
9af3f1fcec | ||
|
|
5b70d55a21 | ||
|
|
9133e337e6 | ||
|
|
9f36fd69ee | ||
|
|
e684f58403 |
@@ -9,10 +9,6 @@ jdk:
|
||||
os:
|
||||
- linux
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
cache:
|
||||
|
||||
@@ -3,7 +3,7 @@ buildscript {
|
||||
maven { url "https://repo.spring.io/plugins-release" }
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
|
||||
classpath("com.bmuschko:gradle-tomcat-plugin:2.2.5")
|
||||
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.7")
|
||||
classpath("io.spring.gradle:spring-io-plugin:0.0.4.RELEASE")
|
||||
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
|
||||
@@ -69,4 +69,4 @@ task docsZip(type: Zip, dependsOn: 'configDocsZip') {
|
||||
|
||||
artifacts {
|
||||
archives docsZip
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,6 @@ liveReload {
|
||||
docRoot asciidoctor.sourceDir.canonicalPath
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url 'http://dist.gemstone.com/maven/release' }
|
||||
}
|
||||
|
||||
asciidoctorj {
|
||||
|
||||
}
|
||||
@@ -31,10 +27,10 @@ dependencies {
|
||||
project(':spring-session-data-mongo'),
|
||||
"org.springframework.data:spring-data-gemfire:$springDataGemFireVersion",
|
||||
"org.springframework.data:spring-data-redis:$springDataRedisVersion",
|
||||
"org.springframework.data:spring-data-gemfire:$springDataGemFireVersion",
|
||||
"org.springframework:spring-websocket:${springVersion}",
|
||||
"org.springframework:spring-messaging:${springVersion}",
|
||||
"org.springframework:spring-jdbc:${springVersion}",
|
||||
"org.springframework.security:spring-security-config:${springSecurityVersion}",
|
||||
"org.springframework.security:spring-security-web:${springSecurityVersion}",
|
||||
"org.springframework.security:spring-security-test:${springSecurityVersion}",
|
||||
'junit:junit:4.11',
|
||||
|
||||
@@ -505,6 +505,43 @@ Before using WebSocket integration, you should be sure that you have <<httpsessi
|
||||
|
||||
include::guides/websocket.adoc[tags=config,leveloffset=+2]
|
||||
|
||||
[[spring-security-concurrent-sessions]]
|
||||
== Spring Security Integration
|
||||
|
||||
Spring Session provides integration with Spring Security to support its concurrent session control.
|
||||
This allows limiting the number of active sessions that a single user can have concurrently, but unlike the default
|
||||
Spring Security support this will also work in a clustered environment. This is done by providing a custom
|
||||
implementation of Spring Security's `SessionRegistry` interface.
|
||||
|
||||
[[spring-security-concurrent-sessions-how]]
|
||||
=== Configuring Spring Security's concurrent session management
|
||||
|
||||
When using Spring Security's Java config DSL, you can configure the custom `SessionRegistry` through the
|
||||
`SessionManagementConfigurer` like this:
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{docs-test-dir}docs/security/SecurityConfiguration.java[tags=class]
|
||||
----
|
||||
|
||||
This assumes that you've also configured Spring Session to provide a `FindByIndexNameSessionRepository` that
|
||||
returns `ExpiringSession` instances.
|
||||
|
||||
When using XML configuration, it would look something like this:
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{docs-test-resources-dir}docs/security/security-config.xml[tags=config]
|
||||
----
|
||||
|
||||
This assumes that your Spring Session `SessionRegistry` bean is called `sessionRegistry`, which is the name used by all
|
||||
`SpringHttpSessionConfiguration` subclasses except for the one for MongoDB: there it's called `mongoSessionRepository`.
|
||||
|
||||
[[spring-security-concurrent-sessions-limitations]]
|
||||
=== Limitations
|
||||
|
||||
Spring Session's implementation of Spring Security's `SessionRegistry` interface does not support the `getAllPrincipals`
|
||||
method, as this information cannot be retrieved using Spring Session. This method is never called by Spring Security,
|
||||
so this only affects applications that access the `SessionRegistry` themselves.
|
||||
|
||||
[[api]]
|
||||
== API Documentation
|
||||
|
||||
@@ -837,7 +874,7 @@ For example, Java Configuration can use the following:
|
||||
include::{docs-test-dir}docs/RedisHttpSessionConfigurationNoOpConfigureRedisActionTests.java[tags=configure-redis-action]
|
||||
----
|
||||
|
||||
XML Configuraiton can use the following:
|
||||
XML Configuration can use the following:
|
||||
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
@@ -1043,6 +1080,7 @@ However, you can override the default `ConversionService` by providing a Bean na
|
||||
|
||||
By default, this implementation uses `SPRING_SESSION` and `SPRING_SESSION_ATTRIBUTES` tables to store sessions.
|
||||
Note that the table name can be easily customized as already described. In that case the table used to store attributes will be named using the provided table name, suffixed with `_ATTRIBUTES`.
|
||||
If further customizations are needed, SQL queries used by the repository can be customized using `set*Query` setter methods. In this case you need to manually configure the `sessionRepository` bean.
|
||||
|
||||
Due to the differences between the various database vendors, especially when it comes to storing binary data, make sure to use SQL script specific to your database.
|
||||
Scripts for most major database vendors are packaged as `org/springframework/session/jdbc/schema-\*.sql`, where `*` is the target database type.
|
||||
|
||||
52
docs/src/test/java/docs/security/SecurityConfiguration.java
Normal file
52
docs/src/test/java/docs/security/SecurityConfiguration.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package docs.security;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
|
||||
|
||||
/**
|
||||
* @author Joris Kuipers
|
||||
*/
|
||||
// tag::class[]
|
||||
@Configuration
|
||||
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
FindByIndexNameSessionRepository<ExpiringSession> sessionRepository;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// other config goes here...
|
||||
.sessionManagement()
|
||||
.maximumSessions(2)
|
||||
.sessionRegistry(sessionRegistry());
|
||||
}
|
||||
|
||||
@Bean
|
||||
SpringSessionBackedSessionRegistry sessionRegistry() {
|
||||
return new SpringSessionBackedSessionRegistry(this.sessionRepository);
|
||||
}
|
||||
}
|
||||
// end::class[]
|
||||
22
docs/src/test/resources/docs/security/security-config.xml
Normal file
22
docs/src/test/resources/docs/security/security-config.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:security="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
|
||||
|
||||
<!-- tag::config[] -->
|
||||
<security:http>
|
||||
<!-- other config goes here... -->
|
||||
<security:session-management>
|
||||
<security:concurrency-control max-sessions="2" session-registry-ref="sessionRegistry"
|
||||
</security:session-management>
|
||||
</security:http>
|
||||
|
||||
<bean id="sessionRegistry"
|
||||
class="org.springframework.session.security.SpringSessionBackedSessionRegistry">
|
||||
<constructor-arg ref="sessionRepository"/>
|
||||
</bean>
|
||||
<!-- end::config[] -->
|
||||
|
||||
</beans>
|
||||
@@ -4,7 +4,7 @@ jacksonVersion=2.6.5
|
||||
jspApiVersion=2.0
|
||||
servletApiVersion=3.0.1
|
||||
jstlelVersion=1.2.5
|
||||
version=1.2.1.RELEASE
|
||||
version=1.3.0.M1
|
||||
springDataRedisVersion=1.7.1.RELEASE
|
||||
html5ShivVersion=3.7.3
|
||||
commonsLoggingVersion=1.2
|
||||
@@ -12,16 +12,18 @@ junitVersion=4.12
|
||||
gebVersion=0.13.1
|
||||
mockitoVersion=1.10.19
|
||||
hazelcastVersion=3.5.4
|
||||
springDataGeodeVersion=1.0.0.APACHE-GEODE-INCUBATING-M2
|
||||
seleniumVersion=2.52.0
|
||||
springSecurityVersion=4.0.3.RELEASE
|
||||
springVersion=4.2.5.RELEASE
|
||||
httpClientVersion=4.5.1
|
||||
jedisVersion=2.8.1
|
||||
h2Version=1.4.191
|
||||
h2Version=1.4.192
|
||||
springDataMongoVersion=1.9.1.RELEASE
|
||||
springShellVersion=1.1.0.RELEASE
|
||||
springDataGemFireVersion=1.8.1.RELEASE
|
||||
assertjVersion=2.3.0
|
||||
assertjVersion=2.5.0
|
||||
spockVersion=1.0-groovy-2.4
|
||||
webjarsTaglibVersion=0.3
|
||||
jstlVersion=1.2.1
|
||||
groovyVersion=2.4.4
|
||||
|
||||
@@ -3,19 +3,18 @@ buildscript {
|
||||
maven { url "https://repo.spring.io/plugins-release" }
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
|
||||
classpath("com.bmuschko:gradle-tomcat-plugin:2.2.5")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'war'
|
||||
apply plugin: 'tomcat'
|
||||
apply plugin: 'com.bmuschko.tomcat'
|
||||
|
||||
[tomcatRun,tomcatRunWar]*.contextPath = '/'
|
||||
|
||||
|
||||
task integrationTomcatRun(type: org.gradle.api.plugins.tomcat.tasks.TomcatRun) {
|
||||
task integrationTomcatRun(type: com.bmuschko.gradle.tomcat.tasks.TomcatRun) {
|
||||
onlyIf { !sourceSets.integrationTest.allSource.empty }
|
||||
buildscriptClasspath = tomcatRun.buildscriptClasspath
|
||||
contextPath = tomcatRun.contextPath
|
||||
daemon = true
|
||||
tomcatClasspath = tomcatRun.tomcatClasspath
|
||||
@@ -36,7 +35,7 @@ task integrationTomcatRun(type: org.gradle.api.plugins.tomcat.tasks.TomcatRun) {
|
||||
}
|
||||
}
|
||||
|
||||
task integrationTomcatStop(type: org.gradle.api.plugins.tomcat.tasks.TomcatStop) {
|
||||
task integrationTomcatStop(type: com.bmuschko.gradle.tomcat.tasks.TomcatStop) {
|
||||
onlyIf { !sourceSets.integrationTest.allSource.empty }
|
||||
doFirst {
|
||||
stopPort = integrationTomcatRun.stopPort
|
||||
@@ -61,4 +60,4 @@ def reservePorts(int count) {
|
||||
def result = sockets*.localPort
|
||||
sockets*.close()
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ apply from: TOMCAT_GRADLE
|
||||
dependencies {
|
||||
def tomcatVersion = '7.0.59'
|
||||
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'
|
||||
}
|
||||
}
|
||||
"org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}",
|
||||
"org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}"
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ dependencies {
|
||||
"org.thymeleaf.extras:thymeleaf-extras-conditionalcomments",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:html5shiv:$html5ShivVersion",
|
||||
"org.webjars:webjars-locator",
|
||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||
"org.springframework.security:spring-security-config:$springSecurityVersion"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<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="@{/webjars/bootstrap/2.3.2/css/bootstrap.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet"></link>
|
||||
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
|
||||
<style type="text/css">
|
||||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
@@ -65,11 +65,11 @@
|
||||
margin-left: 1em;
|
||||
}
|
||||
</style>
|
||||
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
|
||||
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script th:src="@{/webjars/html5shiv/3.7.3/html5shiv.min.js}" src="/webjars/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ dependencies {
|
||||
compile project(':spring-session-data-redis'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
jstlDependencies
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -23,6 +23,7 @@ dependencies {
|
||||
"org.thymeleaf.extras:thymeleaf-extras-conditionalcomments",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:html5shiv:$html5ShivVersion",
|
||||
"org.webjars:webjars-locator",
|
||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
||||
"com.maxmind.geoip2:geoip2:2.3.1",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<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="@{/webjars/bootstrap/2.3.2/css/bootstrap.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet"></link>
|
||||
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
|
||||
<style type="text/css">
|
||||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
@@ -65,11 +65,11 @@
|
||||
margin-left: 1em;
|
||||
}
|
||||
</style>
|
||||
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
|
||||
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script th:src="@{/webjars/html5shiv/3.7.3/html5shiv.min.js}" src="/webjars/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ dependencies {
|
||||
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
"com.hazelcast:hazelcast-client:$hazelcastVersion",
|
||||
jstlDependencies
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Secured Content</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -5,6 +5,7 @@ apply from: SAMPLE_GRADLE
|
||||
dependencies {
|
||||
compile project(':spring-session'),
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
"com.hazelcast:hazelcast-client:$hazelcastVersion",
|
||||
jstlDependencies
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -21,7 +21,8 @@ dependencies {
|
||||
compile project(':spring-session-data-gemfire'),
|
||||
"org.springframework.boot:spring-boot-starter-thymeleaf",
|
||||
"org.springframework.boot:spring-boot-starter-web",
|
||||
"org.webjars:bootstrap:$bootstrapVersion"
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-locator"
|
||||
|
||||
runtime "org.springframework.shell:spring-shell:1.0.0.RELEASE"
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap/2.2.2/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css"/>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -9,9 +9,10 @@ sonarqube {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-data-gemfire'),
|
||||
compile project(':spring-session-data-geode'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
jstlDependencies
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
<url-pattern>/*</url-pattern>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
<dispatcher>ERROR</dispatcher>
|
||||
<dispatcher>ASYNC</dispatcher>
|
||||
</filter-mapping>
|
||||
<!-- end::springSessionRepositoryFilter[] -->
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -7,6 +7,7 @@ dependencies {
|
||||
compile project(':spring-session-data-gemfire'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
jstlDependencies
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
@@ -15,15 +16,15 @@ dependencies {
|
||||
|
||||
integrationTestCompile gebDependencies
|
||||
|
||||
integrationTestRuntime "org.springframework.shell:spring-shell:1.0.0.RELEASE"
|
||||
integrationTestRuntime "org.springframework.shell:spring-shell:1.0.0.RELEASE",
|
||||
"xml-apis:xml-apis:1.4.01"
|
||||
}
|
||||
|
||||
mainClassName = "sample.ServerConfig"
|
||||
|
||||
def port
|
||||
def process
|
||||
|
||||
mainClassName = "sample.ServerConfig"
|
||||
|
||||
task availablePort() << {
|
||||
def serverSocket = new ServerSocket(0)
|
||||
port = serverSocket.localPort
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample;
|
||||
|
||||
/**
|
||||
* The ClassLocator class...
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public final class ClassLocator {
|
||||
|
||||
private ClassLocator() {
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws ClassNotFoundException {
|
||||
String className = "org.w3c.dom.ElementTraversal";
|
||||
//String className = (args.length > 0 ? args[0] : "com.gemstone.gemfire.cache.Cache");
|
||||
Class<?> type = Class.forName(className);
|
||||
String resourceName = type.getName().replaceAll("\\.", "/").concat(".class");
|
||||
System.out.printf("class [%1$s] with resource name [%2$s] is found in [%3$s]%n",
|
||||
className, resourceName, type.getClassLoader().getResource(resourceName));
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -6,6 +6,7 @@ dependencies {
|
||||
compile project(':spring-session-data-gemfire'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
jstlDependencies
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
@@ -23,9 +23,8 @@
|
||||
<filter-mapping>
|
||||
<filter-name>springSessionRepositoryFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
<dispatcher>ERROR</dispatcher>
|
||||
<dispatcher>ASYNC</dispatcher>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
<dispatcher>ERROR</dispatcher>
|
||||
</filter-mapping>
|
||||
<!-- end::springSessionRepositoryFilter[] -->
|
||||
|
||||
@@ -55,4 +54,4 @@
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
</web-app>
|
||||
</web-app>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -3,9 +3,10 @@ apply from: TOMCAT_7_GRADLE
|
||||
apply from: SAMPLE_GRADLE
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-data-gemfire'),
|
||||
compile project(':spring-session-data-geode'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
jstlDependencies
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -23,6 +23,7 @@ dependencies {
|
||||
"org.thymeleaf.extras:thymeleaf-extras-conditionalcomments",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:html5shiv:$html5ShivVersion",
|
||||
"org.webjars:webjars-locator",
|
||||
"com.h2database:h2",
|
||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||
"org.springframework.security:spring-security-config:$springSecurityVersion"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<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="@{/webjars/bootstrap/2.3.2/css/bootstrap.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet"></link>
|
||||
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
|
||||
<style type="text/css">
|
||||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
@@ -65,11 +65,11 @@
|
||||
margin-left: 1em;
|
||||
}
|
||||
</style>
|
||||
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
|
||||
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script th:src="@{/webjars/html5shiv/3.7.3/html5shiv.min.js}" src="/webjars/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
apply from: JAVA_GRADLE
|
||||
apply from: TOMCAT_6_GRADLE
|
||||
apply from: TOMCAT_7_GRADLE
|
||||
apply from: SAMPLE_GRADLE
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-jdbc'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
"com.h2database:h2:$h2Version",
|
||||
jstlDependencies
|
||||
|
||||
|
||||
@@ -23,9 +23,8 @@
|
||||
<filter-mapping>
|
||||
<filter-name>springSessionRepositoryFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
<dispatcher>ERROR</dispatcher>
|
||||
<dispatcher>ASYNC</dispatcher>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
<dispatcher>ERROR</dispatcher>
|
||||
</filter-mapping>
|
||||
<!-- end::springSessionRepositoryFilter[] -->
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -6,6 +6,7 @@ dependencies {
|
||||
compile project(':spring-session-jdbc'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
"com.h2database:h2:$h2Version",
|
||||
jstlDependencies
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
68
samples/httpsession-redis-json/build.gradle
Normal file
68
samples/httpsession-redis-json/build.gradle
Normal file
@@ -0,0 +1,68 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'spring-boot'
|
||||
|
||||
apply from: JAVA_GRADLE
|
||||
|
||||
//tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||
|
||||
group = 'samples'
|
||||
ext {
|
||||
jsonassertVersion="1.3.0"
|
||||
assertjVersion = "2.4.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session'),
|
||||
"org.springframework.boot:spring-boot-starter-redis",
|
||||
"org.springframework.boot:spring-boot-starter-web",
|
||||
"org.springframework.boot:spring-boot-starter-thymeleaf",
|
||||
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
|
||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||
"org.springframework.security:spring-security-core:$springSecurityVersion",
|
||||
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
||||
"org.apache.httpcomponents:httpclient"
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test",
|
||||
"org.assertj:assertj-core:$assertjVersion"
|
||||
testCompile "org.skyscreamer:jsonassert:$jsonassertVersion"
|
||||
testCompile "org.assertj:assertj-core:$assertjVersion"
|
||||
integrationTestCompile gebDependencies,
|
||||
"org.spockframework:spock-spring:$spockVersion"
|
||||
|
||||
}
|
||||
//
|
||||
integrationTest {
|
||||
doFirst {
|
||||
def port = reservePort()
|
||||
|
||||
def host = 'localhost:' + port
|
||||
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/'
|
||||
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
|
||||
systemProperties['server.port'] = port
|
||||
systemProperties['management.port'] = 0
|
||||
|
||||
systemProperties['spring.session.redis.namespace'] = project.name
|
||||
}
|
||||
jvmArgs "-XX:-UseSplitVerifier"
|
||||
}
|
||||
|
||||
integrationTest {
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed"
|
||||
}
|
||||
}
|
||||
|
||||
def reservePort() {
|
||||
def socket = new ServerSocket(0)
|
||||
def result = socket.localPort
|
||||
socket.close()
|
||||
result
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package samples
|
||||
|
||||
import geb.spock.*
|
||||
import sample.Application
|
||||
import samples.pages.*
|
||||
import spock.lang.Stepwise
|
||||
|
||||
import org.springframework.boot.test.IntegrationTest
|
||||
import org.springframework.boot.test.SpringApplicationContextLoader
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.test.context.web.WebAppConfiguration
|
||||
|
||||
/**
|
||||
* @author jitendra on 15/3/16.
|
||||
*/
|
||||
@Stepwise
|
||||
@ContextConfiguration(classes = Application, loader = SpringApplicationContextLoader)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest
|
||||
class HttpRedisJsonTest extends GebSpec {
|
||||
|
||||
def'login page test'() {
|
||||
when:
|
||||
to LoginPage
|
||||
then:
|
||||
at LoginPage
|
||||
}
|
||||
|
||||
def"Unauthenticated user sent to login page"() {
|
||||
when:
|
||||
via HomePage
|
||||
then:
|
||||
at LoginPage
|
||||
}
|
||||
|
||||
def"Successful Login test"() {
|
||||
when:
|
||||
login()
|
||||
then:
|
||||
at HomePage
|
||||
driver.manage().cookies.find {it.name == "SESSION"}
|
||||
!driver.manage().cookies.find {it.name == "JSESSIONID"}
|
||||
}
|
||||
|
||||
def"Set and get attributes in session"() {
|
||||
when:
|
||||
setAttribute("Demo Key", "Demo Value")
|
||||
|
||||
then:
|
||||
at SetAttributePage
|
||||
tdKey()*.text().contains("Demo Key")
|
||||
tdKey()*.text().contains("Demo Value")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package samples.pages
|
||||
|
||||
import geb.Page
|
||||
|
||||
/**
|
||||
* @author jitendra on 15/3/16.
|
||||
*/
|
||||
class HomePage extends Page {
|
||||
static url="/"
|
||||
|
||||
static at=
|
||||
{
|
||||
driver.title == "Spring Session Sample - Home"
|
||||
}
|
||||
|
||||
static content=
|
||||
{
|
||||
form { $('form') }
|
||||
submit { $('button[type=submit]') }
|
||||
setAttribute(required: false) { key = 'project', value = 'SessionRedisJson' ->
|
||||
form.key = key
|
||||
form.value = value
|
||||
submit.click(SetAttributePage)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package samples.pages
|
||||
|
||||
import geb.Page
|
||||
|
||||
/**
|
||||
* @author jitendra on 15/3/16.
|
||||
*/
|
||||
class LoginPage extends Page {
|
||||
static url="/login"
|
||||
|
||||
static at=
|
||||
{
|
||||
assert title == "Spring Session Sample - Login"
|
||||
return true
|
||||
}
|
||||
|
||||
static content=
|
||||
{
|
||||
form { $('form') }
|
||||
submit { $('button[type=submit]') }
|
||||
login(required: false) { user = 'user', pass = 'password' ->
|
||||
form.username = user
|
||||
form.password = pass
|
||||
submit.click(HomePage)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package samples.pages
|
||||
|
||||
import geb.Page
|
||||
|
||||
/**
|
||||
* @author jitendra on 15/3/16.
|
||||
*/
|
||||
class SetAttributePage extends Page {
|
||||
static url="/setValue"
|
||||
|
||||
static at=
|
||||
{
|
||||
title == "Spring Session Sample - Home"
|
||||
}
|
||||
|
||||
static content=
|
||||
{
|
||||
tdKey { $('td') }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package samples;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import sample.Application;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author jitendra on 8/3/16.
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(Application.class)
|
||||
public class RedisSerializerTest {
|
||||
|
||||
@Autowired
|
||||
RedisTemplate<Object, Object> sessionRedisTemplate;
|
||||
|
||||
@Test
|
||||
public void testRedisTemplate() {
|
||||
assertThat(this.sessionRedisTemplate).isNotNull();
|
||||
assertThat(this.sessionRedisTemplate.getDefaultSerializer()).isNotNull();
|
||||
assertThat(this.sessionRedisTemplate.getDefaultSerializer())
|
||||
.isInstanceOf(GenericJackson2JsonRedisSerializer.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* This is a Jackson mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.authentication.AnonymousAuthenticationToken} class.
|
||||
* To use this class you need to register it with
|
||||
* {@link com.fasterxml.jackson.databind.ObjectMapper} and
|
||||
* {@link SimpleGrantedAuthorityMixin} because AnonymousAuthenticationToken contains
|
||||
* SimpleGrantedAuthority. <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CoreJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* <i>Note: This class will save full class name into a property called @class</i>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJacksonModules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class AnonymousAuthenticationTokenMixin {
|
||||
|
||||
/**
|
||||
* Constructor used by Jackson to create object of
|
||||
* {@link org.springframework.security.authentication.AnonymousAuthenticationToken}.
|
||||
*
|
||||
* @param keyHash hashCode of key provided at the time of token creation by using
|
||||
* {@link org.springframework.security.authentication.AnonymousAuthenticationToken#AnonymousAuthenticationToken(String, Object, Collection)}
|
||||
* @param principal the principal (typically a <code>UserDetails</code>)
|
||||
* @param authorities the authorities granted to the principal
|
||||
*/
|
||||
@JsonCreator
|
||||
AnonymousAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash,
|
||||
@JsonProperty("principal") Object principal,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.core.Version;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.RememberMeAuthenticationToken;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* Jackson module for spring-security-core. This module register
|
||||
* {@link AnonymousAuthenticationTokenMixin}, {@link RememberMeAuthenticationTokenMixin},
|
||||
* {@link SimpleGrantedAuthorityMixin}, {@link UnmodifiableSetMixin}, {@link UserMixin}
|
||||
* and {@link UsernamePasswordAuthenticationTokenMixin}. If no default typing enabled by
|
||||
* default then it'll enable it because typing info is needed to properly
|
||||
* serialize/deserialize objects. In order to use this module just add this module into
|
||||
* your ObjectMapper configuration.
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CoreJackson2Module());
|
||||
* </pre> <b>Note: use {@link SecurityJacksonModules#getModules(ClassLoader)} to get list
|
||||
* of all security modules.</b>
|
||||
*
|
||||
* @author Jitendra Singh.
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
public class CoreJackson2Module extends SimpleModule {
|
||||
|
||||
public CoreJackson2Module() {
|
||||
super(CoreJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
SecurityJacksonModules.enableDefaultTyping((ObjectMapper) context.getOwner());
|
||||
context.setMixInAnnotations(AnonymousAuthenticationToken.class,
|
||||
AnonymousAuthenticationTokenMixin.class);
|
||||
context.setMixInAnnotations(RememberMeAuthenticationToken.class,
|
||||
RememberMeAuthenticationTokenMixin.class);
|
||||
context.setMixInAnnotations(SimpleGrantedAuthority.class,
|
||||
SimpleGrantedAuthorityMixin.class);
|
||||
context.setMixInAnnotations(
|
||||
Collections.<Object>unmodifiableSet(Collections.emptySet()).getClass(),
|
||||
UnmodifiableSetMixin.class);
|
||||
context.setMixInAnnotations(User.class, UserMixin.class);
|
||||
context.setMixInAnnotations(UsernamePasswordAuthenticationToken.class,
|
||||
UsernamePasswordAuthenticationTokenMixin.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* This mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.authentication.RememberMeAuthenticationToken}
|
||||
* class. To use this class you need to register it with
|
||||
* {@link com.fasterxml.jackson.databind.ObjectMapper} and 2 more mixin classes.
|
||||
*
|
||||
* <ol>
|
||||
* <li>{@link SimpleGrantedAuthorityMixin}</li>
|
||||
* <li>{@link UserMixin}</li>
|
||||
* <li>{@link UnmodifiableSetMixin}</li>
|
||||
* </ol>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CoreJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* <i>Note: This class will save TypeInfo (full class name) into a property
|
||||
* called @class</i>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class RememberMeAuthenticationTokenMixin {
|
||||
|
||||
/**
|
||||
* Constructor used by Jackson to create
|
||||
* {@link org.springframework.security.authentication.RememberMeAuthenticationToken}
|
||||
* object.
|
||||
*
|
||||
* @param keyHash hashCode of above given key.
|
||||
* @param principal the principal (typically a <code>UserDetails</code>)
|
||||
* @param authorities the authorities granted to the principal
|
||||
*/
|
||||
@JsonCreator
|
||||
RememberMeAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash,
|
||||
@JsonProperty("principal") Object principal,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* This utility class will find all the SecurityModules in classpath.
|
||||
*
|
||||
* <p>
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModules(SecurityJacksonModules.getModules());
|
||||
* </pre> Above code is equivalent to
|
||||
* <p>
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
|
||||
* mapper.registerModule(new CoreJackson2Module());
|
||||
* mapper.registerModule(new CasJackson2Module());
|
||||
* mapper.registerModule(new WebJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh.
|
||||
* @since 4.2
|
||||
*/
|
||||
public final class SecurityJacksonModules {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SecurityJacksonModules.class);
|
||||
private static final List<String> securityJackson2ModuleClasses = Arrays.asList(
|
||||
"org.springframework.security.jackson2.CoreJackson2Module",
|
||||
"org.springframework.security.cas.jackson2.CasJackson2Module",
|
||||
"org.springframework.security.web.jackson2.WebJackson2Module");
|
||||
|
||||
private SecurityJacksonModules() {
|
||||
}
|
||||
|
||||
public static void enableDefaultTyping(ObjectMapper mapper) {
|
||||
if (mapper != null) {
|
||||
TypeResolverBuilder<?> typeBuilder = mapper.getDeserializationConfig()
|
||||
.getDefaultTyper(null);
|
||||
if (typeBuilder == null) {
|
||||
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL,
|
||||
JsonTypeInfo.As.PROPERTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Module loadAndGetInstance(String className, ClassLoader loader) {
|
||||
Module instance = null;
|
||||
try {
|
||||
Class<? extends Module> securityModule = (Class<? extends Module>) ClassUtils
|
||||
.forName(className, loader);
|
||||
if (securityModule != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Loaded module " + className + ", now registering");
|
||||
}
|
||||
instance = securityModule.newInstance();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cannot load module " + className, e);
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loader the ClassLoader to use
|
||||
* @return List of available security modules in classpath.
|
||||
*/
|
||||
public static List<Module> getModules(ClassLoader loader) {
|
||||
List<Module> modules = new ArrayList<Module>();
|
||||
for (String className : securityJackson2ModuleClasses) {
|
||||
Module module = loadAndGetInstance(className, loader);
|
||||
if (module != null) {
|
||||
modules.add(module);
|
||||
}
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* Jackson Mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.core.authority.SimpleGrantedAuthority}.
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CoreJackson2Module());
|
||||
* </pre>
|
||||
* @author Jitendra Singh
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJacksonModules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public abstract class SimpleGrantedAuthorityMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor.
|
||||
* @param role the role
|
||||
*/
|
||||
@JsonCreator
|
||||
public SimpleGrantedAuthorityMixin(@JsonProperty("authority") String role) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* This mixin class used to deserialize java.util.Collections$UnmodifiableSet and used
|
||||
* with various AuthenticationToken implementation's mixin classes.
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CoreJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJacksonModules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
class UnmodifiableSetMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor
|
||||
* @param s the Set
|
||||
*/
|
||||
@JsonCreator
|
||||
UnmodifiableSetMixin(Set<?> s) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.MissingNode;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* Custom Deserializer for {@link User} class. This is already registered with
|
||||
* {@link UserMixin}. You can also use it directly with your mixin class.
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see UserMixin
|
||||
*/
|
||||
class UserDeserializer extends JsonDeserializer<User> {
|
||||
|
||||
/**
|
||||
* This method will create {@link User} object. It will ensure successful object
|
||||
* creation even if password key is null in serialized json, because credentials may
|
||||
* be removed from the {@link User} by invoking {@link User#eraseCredentials()}. In
|
||||
* that case there won't be any password key in serialized json.
|
||||
*
|
||||
* @param jp the JsonParser
|
||||
* @param ctxt the DeserializationContext
|
||||
* @return the user
|
||||
* @throws IOException if a exception during IO occurs
|
||||
* @throws JsonProcessingException if an error during JSON processing occurs
|
||||
*/
|
||||
@Override
|
||||
public User deserialize(JsonParser jp, DeserializationContext ctxt)
|
||||
throws IOException, JsonProcessingException {
|
||||
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
|
||||
JsonNode jsonNode = mapper.readTree(jp);
|
||||
Set<GrantedAuthority> authorities = mapper.convertValue(
|
||||
jsonNode.get("authorities"),
|
||||
new TypeReference<Set<SimpleGrantedAuthority>>() {
|
||||
});
|
||||
JsonNode password = readJsonNode(jsonNode, "password");
|
||||
User result = new User(readJsonNode(jsonNode, "username").asText(),
|
||||
password.asText(""), readJsonNode(jsonNode, "enabled").asBoolean(),
|
||||
readJsonNode(jsonNode, "accountNonExpired").asBoolean(),
|
||||
readJsonNode(jsonNode, "credentialsNonExpired").asBoolean(),
|
||||
readJsonNode(jsonNode, "accountNonLocked").asBoolean(), authorities);
|
||||
|
||||
if (password.asText(null) == null) {
|
||||
result.eraseCredentials();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private JsonNode readJsonNode(JsonNode jsonNode, String field) {
|
||||
return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
/**
|
||||
* This mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.core.userdetails.User}. This class also register a
|
||||
* custom deserializer {@link UserDeserializer} to deserialize User object successfully.
|
||||
* In order to use this mixin you need to register two more mixin classes in your
|
||||
* ObjectMapper configuration.
|
||||
* <ol>
|
||||
* <li>{@link SimpleGrantedAuthorityMixin}</li>
|
||||
* <li>{@link UnmodifiableSetMixin}</li>
|
||||
* </ol>
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CoreJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see UserDeserializer
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJacksonModules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonDeserialize(using = UserDeserializer.class)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
abstract class UserMixin {
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.MissingNode;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* Custom deserializer for {@link UsernamePasswordAuthenticationToken}. At the time of
|
||||
* deserialization it will invoke suitable constructor depending on the value of
|
||||
* <b>authenticated</b> property. It will ensure that the token's state must not change.
|
||||
* <p>
|
||||
* This deserializer is already registered with
|
||||
* {@link UsernamePasswordAuthenticationTokenMixin} but you can also registered it with
|
||||
* your own mixin class.
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see UsernamePasswordAuthenticationTokenMixin
|
||||
*/
|
||||
class UsernamePasswordAuthenticationTokenDeserializer
|
||||
extends JsonDeserializer<UsernamePasswordAuthenticationToken> {
|
||||
|
||||
/**
|
||||
* This method construct {@link UsernamePasswordAuthenticationToken} object from
|
||||
* serialized json.
|
||||
* @param jp the JsonParser
|
||||
* @param ctxt the DeserializationContext
|
||||
* @return the user
|
||||
* @throws IOException if a exception during IO occurs
|
||||
* @throws JsonProcessingException if an error during JSON processing occurs
|
||||
*/
|
||||
@Override
|
||||
public UsernamePasswordAuthenticationToken deserialize(JsonParser jp,
|
||||
DeserializationContext ctxt) throws IOException, JsonProcessingException {
|
||||
UsernamePasswordAuthenticationToken token = null;
|
||||
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
|
||||
JsonNode jsonNode = mapper.readTree(jp);
|
||||
Boolean authenticated = readJsonNode(jsonNode, "authenticated").asBoolean();
|
||||
JsonNode principalNode = readJsonNode(jsonNode, "principal");
|
||||
Object principal = null;
|
||||
if (principalNode.isObject()) {
|
||||
principal = mapper.readValue(principalNode.toString(),
|
||||
new TypeReference<User>() {
|
||||
});
|
||||
}
|
||||
else {
|
||||
principal = principalNode.asText();
|
||||
}
|
||||
Object credentials = readJsonNode(jsonNode, "credentials").asText();
|
||||
List<GrantedAuthority> authorities = mapper.readValue(
|
||||
readJsonNode(jsonNode, "authorities").toString(),
|
||||
new TypeReference<List<GrantedAuthority>>() {
|
||||
});
|
||||
if (authenticated) {
|
||||
token = new UsernamePasswordAuthenticationToken(principal, credentials,
|
||||
authorities);
|
||||
}
|
||||
else {
|
||||
token = new UsernamePasswordAuthenticationToken(principal, credentials);
|
||||
}
|
||||
token.setDetails(readJsonNode(jsonNode, "details"));
|
||||
return token;
|
||||
}
|
||||
|
||||
private JsonNode readJsonNode(JsonNode jsonNode, String field) {
|
||||
return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize / deserialize
|
||||
* {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken}
|
||||
* . This class register a custom deserializer
|
||||
* {@link UsernamePasswordAuthenticationTokenDeserializer}.
|
||||
*
|
||||
* In order to use this mixin you'll need to add 3 more mixin classes.
|
||||
* <ol>
|
||||
* <li>{@link UnmodifiableSetMixin}</li>
|
||||
* <li>{@link SimpleGrantedAuthorityMixin}</li>
|
||||
* <li>{@link UserMixin}</li>
|
||||
* </ol>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CoreJackson2Module());
|
||||
* </pre>
|
||||
* @author Jitendra Singh
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJacksonModules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonDeserialize(using = UsernamePasswordAuthenticationTokenDeserializer.class)
|
||||
abstract class UsernamePasswordAuthenticationTokenMixin {
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mix-in classes to add Jackson serialization support.
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
/**
|
||||
* Package contains Jackson mixin classes.
|
||||
*/
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.jackson2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.MissingNode;
|
||||
import com.fasterxml.jackson.databind.node.NullNode;
|
||||
|
||||
/**
|
||||
* Jackson deserializer for {@link Cookie}. This is needed because in most cases we don't
|
||||
* set {@link Cookie#getDomain()} property. So when jackson deserialize that json
|
||||
* {@link Cookie#setDomain(String)} throws {@link NullPointerException}. This is
|
||||
* registered with {@link CookieMixin} but you can also use it with your own mixin.
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see CookieMixin
|
||||
*/
|
||||
class CookieDeserializer extends JsonDeserializer<Cookie> {
|
||||
|
||||
@Override
|
||||
public Cookie deserialize(JsonParser jp, DeserializationContext ctxt)
|
||||
throws IOException, JsonProcessingException {
|
||||
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
|
||||
JsonNode jsonNode = mapper.readTree(jp);
|
||||
Cookie cookie = new Cookie(readJsonNode(jsonNode, "name").asText(),
|
||||
readJsonNode(jsonNode, "value").asText());
|
||||
cookie.setComment(readJsonNode(jsonNode, "comment").asText());
|
||||
cookie.setDomain(readJsonNode(jsonNode, "domain").asText());
|
||||
cookie.setMaxAge(readJsonNode(jsonNode, "maxAge").asInt(-1));
|
||||
cookie.setSecure(readJsonNode(jsonNode, "secure").asBoolean());
|
||||
cookie.setVersion(readJsonNode(jsonNode, "version").asInt());
|
||||
cookie.setPath(readJsonNode(jsonNode, "path").asText());
|
||||
cookie.setHttpOnly(readJsonNode(jsonNode, "httpOnly").asBoolean());
|
||||
return cookie;
|
||||
}
|
||||
|
||||
private JsonNode readJsonNode(JsonNode jsonNode, String field) {
|
||||
return jsonNode.has(field) && !(jsonNode.get(field) instanceof NullNode)
|
||||
? jsonNode.get(field) : MissingNode.getInstance();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.jackson2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
/**
|
||||
* Mixin class to serialize/deserialize {@link javax.servlet.http.Cookie}
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new WebJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see WebJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJacksonModules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonDeserialize(using = CookieDeserializer.class)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class CookieMixin {
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.jackson2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* Jackson mixin class to serialize/deserialize
|
||||
* {@link org.springframework.security.web.csrf.DefaultCsrfToken} serialization support.
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new WebJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see WebJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJacksonModules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class DefaultCsrfTokenMixin {
|
||||
|
||||
/**
|
||||
* JsonCreator constructor needed by Jackson to create
|
||||
* {@link org.springframework.security.web.csrf.DefaultCsrfToken} object.
|
||||
*
|
||||
* @param headerName the name of the header
|
||||
* @param parameterName the parameter name
|
||||
* @param token the CSRF token value
|
||||
*/
|
||||
@JsonCreator
|
||||
DefaultCsrfTokenMixin(@JsonProperty("headerName") String headerName,
|
||||
@JsonProperty("parameterName") String parameterName,
|
||||
@JsonProperty("token") String token) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.jackson2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
|
||||
|
||||
/**
|
||||
* Spring Security 4.2 will support saved request.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class DefaultSavedRequestBuilder {
|
||||
|
||||
public DefaultSavedRequest build() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.jackson2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
|
||||
|
||||
/**
|
||||
* Jackson mixin class to serialize/deserialize {@link DefaultSavedRequest}. This mixin
|
||||
* use {@link org.springframework.security.web.savedrequest.DefaultSavedRequest.Builder}
|
||||
* to deserialized json.In order to use this mixin class you also need to register
|
||||
* {@link CookieMixin}.
|
||||
* <p>
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new WebJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see WebJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJacksonModules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonDeserialize(builder = DefaultSavedRequestBuilder.class)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class DefaultSavedRequestMixin {
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.jackson2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* Jackson mixin class to serialize/deserialize
|
||||
* {@link org.springframework.security.web.savedrequest.SavedCookie} serialization
|
||||
* support.
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new WebJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh.
|
||||
* @see WebJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJacksonModules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
abstract class SavedCookieMixin {
|
||||
|
||||
@JsonCreator
|
||||
SavedCookieMixin(@JsonProperty("name") String name,
|
||||
@JsonProperty("value") String value, @JsonProperty("comment") String comment,
|
||||
@JsonProperty("domain") String domain, @JsonProperty("maxAge") int maxAge,
|
||||
@JsonProperty("path") String path, @JsonProperty("secure") boolean secure,
|
||||
@JsonProperty("version") int version) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.jackson2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* Jackson mixin class to serialize/deserialize
|
||||
* {@link org.springframework.security.web.authentication.WebAuthenticationDetails}.
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new WebJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see WebJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJacksonModules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
class WebAuthenticationDetailsMixin {
|
||||
|
||||
@JsonCreator
|
||||
WebAuthenticationDetailsMixin(@JsonProperty("remoteAddress") String remoteAddress,
|
||||
@JsonProperty("sessionId") String sessionId) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.jackson2;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import com.fasterxml.jackson.core.Version;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
import org.springframework.security.jackson2.SecurityJacksonModules;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||
import org.springframework.security.web.csrf.DefaultCsrfToken;
|
||||
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
|
||||
import org.springframework.security.web.savedrequest.SavedCookie;
|
||||
|
||||
/**
|
||||
* Jackson module for spring-security-web. This module register {@link CookieMixin},
|
||||
* {@link DefaultCsrfTokenMixin}, {@link DefaultSavedRequestMixin} and
|
||||
* {@link WebAuthenticationDetailsMixin}. If no default typing enabled by default then
|
||||
* it'll enable it because typing info is needed to properly serialize/deserialize
|
||||
* objects. In order to use this module just add this module into your ObjectMapper
|
||||
* configuration.
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new WebJackson2Module());
|
||||
* </pre> <b>Note: use {@link SecurityJacksonModules#getModules(ClassLoader)} to get list
|
||||
* of all security modules.</b>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
public class WebJackson2Module extends SimpleModule {
|
||||
|
||||
public WebJackson2Module() {
|
||||
super(WebJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
SecurityJacksonModules.enableDefaultTyping((ObjectMapper) context.getOwner());
|
||||
context.setMixInAnnotations(Cookie.class, CookieMixin.class);
|
||||
context.setMixInAnnotations(SavedCookie.class, SavedCookieMixin.class);
|
||||
context.setMixInAnnotations(DefaultCsrfToken.class, DefaultCsrfTokenMixin.class);
|
||||
context.setMixInAnnotations(DefaultSavedRequest.class,
|
||||
DefaultSavedRequestMixin.class);
|
||||
context.setMixInAnnotations(WebAuthenticationDetails.class,
|
||||
WebAuthenticationDetailsMixin.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mix-in classes to provide Jackson serialization support.
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
package org.springframework.security.web.jackson2;
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author jitendra on 3/3/16.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
/**
|
||||
* @author jitendra on 3/3/16.
|
||||
*/
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
// @formatter:off
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.antMatchers("/resources/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginPage("/login")
|
||||
.permitAll()
|
||||
.and()
|
||||
.logout().permitAll();
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER");
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.security.jackson2.SecurityJacksonModules;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
|
||||
/**
|
||||
* @author jitendra on 3/3/16.
|
||||
*/
|
||||
@EnableRedisHttpSession
|
||||
public class SessionConfig implements BeanClassLoaderAware {
|
||||
|
||||
private ClassLoader loader;
|
||||
|
||||
@Bean
|
||||
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
|
||||
return new GenericJackson2JsonRedisSerializer(objectMapper());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JedisConnectionFactory connectionFactory() {
|
||||
return new JedisConnectionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Customized {@link ObjectMapper} to add mix-in for class that doesn't have default
|
||||
* constructors
|
||||
*
|
||||
* @return the {@link ObjectMapper} to use
|
||||
*/
|
||||
ObjectMapper objectMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.registerModules(SecurityJacksonModules.getModules(this.loader));
|
||||
return mapper;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang
|
||||
* .ClassLoader)
|
||||
*/
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.loader = classLoader;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.web;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* @author jitendra on 5/3/16.
|
||||
*/
|
||||
@Controller
|
||||
public class HomeController {
|
||||
|
||||
@RequestMapping("/")
|
||||
public String home() {
|
||||
return "home";
|
||||
}
|
||||
|
||||
@RequestMapping("/setValue")
|
||||
public String setValue(@RequestParam(name = "key", required = false) String key,
|
||||
@RequestParam(name = "value", required = false) String value,
|
||||
HttpServletRequest request) {
|
||||
if (!ObjectUtils.isEmpty(key) && !ObjectUtils.isEmpty(value)) {
|
||||
request.getSession().setAttribute(key, value);
|
||||
}
|
||||
return "home";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.web;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* @author jitendra on 3/3/16.
|
||||
*/
|
||||
@Controller
|
||||
public class LoginController {
|
||||
|
||||
@RequestMapping("/login")
|
||||
public String login() {
|
||||
return "login";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
spring.thymeleaf.cache=false
|
||||
spring.template.cache=false
|
||||
#logging.level.org.springframework.security=DEBUG
|
||||
1092
samples/httpsession-redis-json/src/main/resources/static/resources/css/bootstrap-responsive.css
vendored
Normal file
1092
samples/httpsession-redis-json/src/main/resources/static/resources/css/bootstrap-responsive.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6039
samples/httpsession-redis-json/src/main/resources/static/resources/css/bootstrap.css
vendored
Normal file
6039
samples/httpsession-redis-json/src/main/resources/static/resources/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="layout">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Home</title>
|
||||
<link th:href="@{/resources/css/bootstrap.css}" href="../static/resources/css/bootstrap.css" rel="stylesheet"></link>
|
||||
<link th:href="@{resources/css/bootstrap-responsive.css}" href="/static/resources/css/bootstrap-responsive.css" rel="stylesheet"></link>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<form class="form-inline" name="f" th:action="@{/setValue}" method="post">
|
||||
<fieldset>
|
||||
<legend>Add value to session</legend>
|
||||
<input type="text" id="key" name="key" placeholder="key"/>
|
||||
<input type="text" id="value" name="value" placeholder="value"/>
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<div layout:fragment="table-content">
|
||||
<table class="table table-bordered" style="table-layout: fixed; word-wrap: break-word;">
|
||||
<tr>
|
||||
<th>Attribute Name</th>
|
||||
<th>Attribute Value</th>
|
||||
</tr>
|
||||
<tr th:each="name : ${T(java.util.Collections).list(#httpSession.getAttributeNames())}">
|
||||
<td th:text="${name}"></td>
|
||||
<td th:text="${#httpSession.getAttribute(name)}"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,123 @@
|
||||
<!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
|
||||
-------------------------------------------------- */
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
/* The html and body elements cannot have any padding or margin. */
|
||||
}
|
||||
|
||||
/* Wrapper for page content to push down footer */
|
||||
#wrap {
|
||||
min-height: 100%;
|
||||
height: auto !important;
|
||||
height: 100%;
|
||||
/* Negative indent footer by it's height */
|
||||
margin: 0 auto -60px;
|
||||
}
|
||||
|
||||
/* Set the fixed height of the footer here */
|
||||
#push,
|
||||
#footer {
|
||||
height: 60px;
|
||||
}
|
||||
#footer {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* Lastly, apply responsive CSS fixes as necessary */
|
||||
@media (max-width: 767px) {
|
||||
#footer {
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Custom page CSS
|
||||
-------------------------------------------------- */
|
||||
/* Not required for template or sticky footer method. */
|
||||
|
||||
.container {
|
||||
width: auto;
|
||||
max-width: 680px;
|
||||
}
|
||||
.container .credit {
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
a {
|
||||
color: green;
|
||||
}
|
||||
.navbar-form {
|
||||
margin-left: 1em;
|
||||
}
|
||||
</style>
|
||||
<link th:href="@{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>
|
||||
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Begin page content -->
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-success"
|
||||
th:if="${globalMessage}"
|
||||
th:text="${globalMessage}">
|
||||
Some Success message
|
||||
</div>
|
||||
<div layout:fragment="content">
|
||||
Fake content
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<div layout:fragment="table-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="push"><!-- --></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="layout">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Login</title>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<form name="f" th:action="@{/login}" method="post">
|
||||
<fieldset>
|
||||
<legend>Please Login - </legend>
|
||||
<div th:if="${param.error}" class="alert alert-error">Invalid
|
||||
username and password.</div>
|
||||
<div th:if="${param.logout}" class="alert alert-success">You
|
||||
have been logged out.</div>
|
||||
<label for="username">Username</label> <input type="text"
|
||||
id="username" name="username" /> <label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" />
|
||||
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn">Log in</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="container" layout:fragment="table-content">
|
||||
<p>
|
||||
This demo use GenericJackson2JsonRedisSerializer as DefaultRedis Serializer.
|
||||
<br/>
|
||||
To login use <b>user</b> as username and <b>password</b> as Password.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +1,12 @@
|
||||
apply from: JAVA_GRADLE
|
||||
apply from: TOMCAT_6_GRADLE
|
||||
apply from: TOMCAT_7_GRADLE
|
||||
apply from: SAMPLE_GRADLE
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-data-redis'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
jstlDependencies
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
<filter-mapping>
|
||||
<filter-name>springSessionRepositoryFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
<dispatcher>ERROR</dispatcher>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
<dispatcher>ERROR</dispatcher>
|
||||
</filter-mapping>
|
||||
<!-- end::springSessionRepositoryFilter[] -->
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -6,6 +6,7 @@ dependencies {
|
||||
compile project(':spring-session-data-redis'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
jstlDependencies
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -23,6 +23,7 @@ dependencies {
|
||||
"org.thymeleaf.extras:thymeleaf-extras-conditionalcomments",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:html5shiv:$html5ShivVersion",
|
||||
"org.webjars:webjars-locator",
|
||||
"de.flapdoodle.embed:de.flapdoodle.embed.mongo",
|
||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||
"org.springframework.security:spring-security-config:$springSecurityVersion"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<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="@{/webjars/bootstrap/2.3.2/css/bootstrap.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet"></link>
|
||||
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
|
||||
<style type="text/css">
|
||||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
@@ -65,11 +65,11 @@
|
||||
margin-left: 1em;
|
||||
}
|
||||
</style>
|
||||
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
|
||||
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script th:src="@{/webjars/html5shiv/3.7.3/html5shiv.min.js}" src="/webjars/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ dependencies {
|
||||
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
jstlDependencies
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion",
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Secured Content</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
|
||||
@@ -6,6 +6,7 @@ dependencies {
|
||||
compile project(':spring-session-data-redis'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.webjars:bootstrap:3.3.6",
|
||||
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
|
||||
jstlDependencies
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Demonstrates Multi User Log In</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/3.3.6/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
@@ -100,8 +102,10 @@
|
||||
<!-- Bootstrap core JavaScript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="<c:url value="/webjars/jquery/1.11.1/jquery.min.js"/>"></script>
|
||||
<script src="<c:url value="/webjars/bootstrap/3.3.6/js/bootstrap.min.js"/>"></script>
|
||||
<wj:locate path="jquery.min.js" relativeTo="META-INF/resources" var="jqueryLocation"/>
|
||||
<script src="<c:url value="${jqueryLocation}"/>"></script>
|
||||
<wj:locate path="bootstrap.min.js" relativeTo="META-INF/resources" var="bootstrapJsLocation"/>
|
||||
<script src="<c:url value="${bootstrapJsLocation}"/>"></script>
|
||||
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
|
||||
<script src="<c:url value="/assets/js/ie10-viewport-bug-workaround.js"/>"></script>
|
||||
</body>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Linked Page</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/3.3.6/css/bootstrap.min.css"/>">
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
@@ -73,8 +75,10 @@
|
||||
<!-- Bootstrap core JavaScript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="<c:url value="/webjars/jquery/1.11.1/jquery.min.js"/>"></script>
|
||||
<script src="<c:url value="/webjars/bootstrap/3.3.6/js/bootstrap.min.js"/>"></script>
|
||||
<wj:locate path="jquery.min.js" relativeTo="META-INF/resources" var="jqueryLocation"/>
|
||||
<script src="<c:url value="${jqueryLocation}"/>"></script>
|
||||
<wj:locate path="bootstrap.min.js" relativeTo="META-INF/resources" var="bootstrapJsLocation"/>
|
||||
<script src="<c:url value="${bootstrapJsLocation}"/>"></script>
|
||||
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
|
||||
<script src="<c:url value="/assets/js/ie10-viewport-bug-workaround.js"/>"></script>
|
||||
</body>
|
||||
|
||||
@@ -30,6 +30,7 @@ dependencies {
|
||||
"org.webjars:knockout:2.3.0",
|
||||
"org.webjars:sockjs-client:0.3.4",
|
||||
"org.webjars:stomp-websocket:2.3.0",
|
||||
"org.webjars:webjars-locator",
|
||||
"com.h2database:h2",
|
||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
||||
|
||||
@@ -49,11 +49,11 @@
|
||||
|
||||
|
||||
<!-- 3rd party -->
|
||||
<script th:src="@{/webjars/jquery/1.9.0/jquery.min.js}" src="/webjars/jquery/1.9.0/jquery.min.js"></script>
|
||||
<script th:src="@{/webjars/bootstrap/2.3.2/js/bootstrap.min.js}" src="/webjars/bootstrap/2.3.2/js/bootstrap.min.js"></script>
|
||||
<script th:src="@{/webjars/knockout/2.3.0/knockout.js}" src="/webjars/knockout/2.3.0/knockout.js"></script>
|
||||
<script th:src="@{/webjars/sockjs-client/0.3.4/sockjs.min.js}" src="/webjars/sockjs-client/0.3.4/sockjs.min.js"></script>
|
||||
<script th:src="@{/webjars/stomp-websocket/2.3.0/stomp.min.js}" src="/webjars/stomp-websocket/2.3.0/stomp.min.js"></script>
|
||||
<script th:src="@{/webjars/jquery/jquery.min.js}" src="/webjars/jquery/jquery.min.js"></script>
|
||||
<script th:src="@{/webjars/bootstrap/js/bootstrap.min.js}" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script th:src="@{/webjars/knockout/knockout.js}" src="/webjars/knockout/knockout.js"></script>
|
||||
<script th:src="@{/webjars/sockjs-client/sockjs.min.js}" src="/webjars/sockjs-client/sockjs.min.js"></script>
|
||||
<script th:src="@{/webjars/stomp-websocket/stomp.min.js}" src="/webjars/stomp-websocket/stomp.min.js"></script>
|
||||
|
||||
<!-- application -->
|
||||
<script src="resources/js/message.js"></script>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<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="@{/webjars/bootstrap/2.3.2/css/bootstrap.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet"></link>
|
||||
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
|
||||
<style type="text/css">
|
||||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
@@ -65,11 +65,11 @@
|
||||
margin-left: 1em;
|
||||
}
|
||||
</style>
|
||||
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
|
||||
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script th:src="@{/webjars/html5shiv/3.7.3/html5shiv.min.js}" src="/webjars/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ include 'samples:httpsession-gemfire-p2p-xml'
|
||||
include 'samples:httpsession-jdbc'
|
||||
include 'samples:httpsession-jdbc-boot'
|
||||
include 'samples:httpsession-jdbc-xml'
|
||||
include 'samples:httpsession-redis-json'
|
||||
include 'samples:httpsession-xml'
|
||||
include 'samples:rest'
|
||||
include 'samples:security'
|
||||
@@ -26,6 +27,7 @@ include 'samples:grails3'
|
||||
|
||||
include 'spring-session'
|
||||
include 'spring-session-data-gemfire'
|
||||
include 'spring-session-data-geode'
|
||||
include 'spring-session-data-redis'
|
||||
include 'spring-session-jdbc'
|
||||
include 'spring-session-data-mongo'
|
||||
|
||||
21
spring-session-data-geode/build.gradle
Normal file
21
spring-session-data-geode/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
||||
apply from: JAVA_GRADLE
|
||||
apply from: MAVEN_GRADLE
|
||||
apply plugin: 'spring-io'
|
||||
|
||||
description = "Aggregator for Spring Session and Spring Data GemFire with Apache Geode support"
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session')
|
||||
compile("org.springframework.data:spring-data-geode:$springDataGeodeVersion") {
|
||||
exclude group: "org.slf4j", module: 'slf4j-api'
|
||||
exclude group: "org.slf4j", module: 'jcl-over-slf4j'
|
||||
}
|
||||
}
|
||||
|
||||
dependencyManagement {
|
||||
springIoTestRuntime {
|
||||
imports {
|
||||
mavenBom "io.spring.platform:platform-bom:${springIoVersion}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,15 @@ dependencies {
|
||||
"org.springframework:spring-context:$springVersion",
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.springframework:spring-messaging:$springVersion",
|
||||
"org.springframework:spring-websocket:$springVersion"
|
||||
"org.springframework:spring-websocket:$springVersion",
|
||||
"org.springframework.security:spring-security-core:$springSecurityVersion"
|
||||
provided "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
integrationTestCompile "redis.clients:jedis:$jedisVersion",
|
||||
"org.apache.commons:commons-pool2:2.2",
|
||||
"com.hazelcast:hazelcast-client:$hazelcastVersion",
|
||||
"com.h2database:h2:$h2Version",
|
||||
"org.hsqldb:hsqldb:2.3.3",
|
||||
"org.apache.derby:derby:10.12.1.1",
|
||||
"de.flapdoodle.embed:de.flapdoodle.embed.mongo:1.50.2"
|
||||
|
||||
integrationTestRuntime "org.springframework.shell:spring-shell:1.0.0.RELEASE"
|
||||
@@ -42,7 +45,6 @@ dependencies {
|
||||
"org.springframework.security:spring-security-core:$springSecurityVersion"
|
||||
|
||||
jacoco "org.jacoco:org.jacoco.agent:0.7.2.201409121644:runtime"
|
||||
|
||||
}
|
||||
|
||||
dependencyManagement {
|
||||
|
||||
@@ -93,17 +93,15 @@ public abstract class AbstractGemFireIntegrationTests {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
System.setProperty("gemfire.Query.VERBOSE",
|
||||
String.valueOf(isQueryDebuggingEnabled()));
|
||||
System.setProperty("gemfire.Query.VERBOSE", String.valueOf(isQueryDebuggingEnabled()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static File createDirectory(String pathname) {
|
||||
File directory = new File(WORKING_DIRECTORY, pathname);
|
||||
|
||||
assertThat(directory.isDirectory() || directory.mkdirs())
|
||||
.as(String.format("Failed to create directory (%1$s)", directory))
|
||||
.isTrue();
|
||||
assertThat(directory.isDirectory() || directory.mkdirs()).as(
|
||||
String.format("Failed to create directory (%1$s)", directory)).isTrue();
|
||||
|
||||
directory.deleteOnExit();
|
||||
|
||||
@@ -111,8 +109,7 @@ public abstract class AbstractGemFireIntegrationTests {
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static List<String> createJavaProcessCommandLine(Class<?> type,
|
||||
String... args) {
|
||||
protected static List<String> createJavaProcessCommandLine(Class<?> type, String... args) {
|
||||
List<String> commandLine = new ArrayList<String>();
|
||||
|
||||
String javaHome = System.getProperty("java.home");
|
||||
@@ -123,8 +120,7 @@ public abstract class AbstractGemFireIntegrationTests {
|
||||
commandLine.add("-ea");
|
||||
commandLine.add(String.format("-Dgemfire.log-file=%1$s", GEMFIRE_LOG_FILE_NAME));
|
||||
commandLine.add(String.format("-Dgemfire.log-level=%1$s", GEMFIRE_LOG_LEVEL));
|
||||
commandLine
|
||||
.add(String.format("-Dgemfire.Query.VERBOSE=%1$s", GEMFIRE_QUERY_DEBUG));
|
||||
commandLine.add(String.format("-Dgemfire.Query.VERBOSE=%1$s", GEMFIRE_QUERY_DEBUG));
|
||||
commandLine.addAll(extractJvmArguments(args));
|
||||
commandLine.add("-classpath");
|
||||
commandLine.add(System.getProperty("java.class.path"));
|
||||
@@ -163,10 +159,8 @@ public abstract class AbstractGemFireIntegrationTests {
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static Process run(Class<?> type, File directory, String... args)
|
||||
throws IOException {
|
||||
return new ProcessBuilder().command(createJavaProcessCommandLine(type, args))
|
||||
.directory(directory).start();
|
||||
protected static Process run(Class<?> type, File directory, String... args) throws IOException {
|
||||
return new ProcessBuilder().command(createJavaProcessCommandLine(type, args)).directory(directory).start();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@@ -175,10 +169,8 @@ public abstract class AbstractGemFireIntegrationTests {
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(CacheServer cacheServer,
|
||||
long duration) {
|
||||
return waitForCacheServerToStart(cacheServer.getBindAddress(),
|
||||
cacheServer.getPort(), duration);
|
||||
protected static boolean waitForCacheServerToStart(CacheServer cacheServer, long duration) {
|
||||
return waitForCacheServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), duration);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@@ -187,8 +179,7 @@ public abstract class AbstractGemFireIntegrationTests {
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(final String host, final int port,
|
||||
long duration) {
|
||||
protected static boolean waitForCacheServerToStart(final String host, final int port, long duration) {
|
||||
return waitOnCondition(new Condition() {
|
||||
AtomicBoolean connected = new AtomicBoolean(false);
|
||||
|
||||
@@ -248,8 +239,7 @@ public abstract class AbstractGemFireIntegrationTests {
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
protected static boolean waitForProcessToStart(Process process, File directory,
|
||||
long duration) {
|
||||
protected static boolean waitForProcessToStart(Process process, File directory, long duration) {
|
||||
final File processControl = new File(directory, DEFAULT_PROCESS_CONTROL_FILENAME);
|
||||
|
||||
waitOnCondition(new Condition() {
|
||||
@@ -267,8 +257,7 @@ public abstract class AbstractGemFireIntegrationTests {
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static int waitForProcessToStop(Process process, File directory,
|
||||
long duration) {
|
||||
protected static int waitForProcessToStop(Process process, File directory, long duration) {
|
||||
final long timeout = (System.currentTimeMillis() + duration);
|
||||
|
||||
try {
|
||||
@@ -323,30 +312,24 @@ public abstract class AbstractGemFireIntegrationTests {
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected void assertRegion(Region<?, ?> actualRegion, String expectedName,
|
||||
DataPolicy expectedDataPolicy) {
|
||||
protected void assertRegion(Region<?, ?> actualRegion, String expectedName, DataPolicy expectedDataPolicy) {
|
||||
assertThat(actualRegion).isNotNull();
|
||||
assertThat(actualRegion.getName()).isEqualTo(expectedName);
|
||||
assertThat(actualRegion.getFullPath())
|
||||
.isEqualTo(GemFireUtils.toRegionPath(expectedName));
|
||||
assertThat(actualRegion.getFullPath()).isEqualTo(GemFireUtils.toRegionPath(expectedName));
|
||||
assertThat(actualRegion.getAttributes()).isNotNull();
|
||||
assertThat(actualRegion.getAttributes().getDataPolicy())
|
||||
.isEqualTo(expectedDataPolicy);
|
||||
assertThat(actualRegion.getAttributes().getDataPolicy()).isEqualTo(expectedDataPolicy);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected void assertIndex(Index index, String expectedExpression,
|
||||
String expectedFromClause) {
|
||||
protected void assertIndex(Index index, String expectedExpression, String expectedFromClause) {
|
||||
assertThat(index).isNotNull();
|
||||
assertThat(index.getIndexedExpression()).isEqualTo(expectedExpression);
|
||||
assertThat(index.getFromClause()).isEqualTo(expectedFromClause);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected void assertEntryIdleTimeout(Region<?, ?> region,
|
||||
ExpirationAction expectedAction, int expectedTimeout) {
|
||||
assertEntryIdleTimeout(region.getAttributes().getEntryIdleTimeout(),
|
||||
expectedAction, expectedTimeout);
|
||||
protected void assertEntryIdleTimeout(Region<?, ?> region, ExpirationAction expectedAction, int expectedTimeout) {
|
||||
assertEntryIdleTimeout(region.getAttributes().getEntryIdleTimeout(), expectedAction, expectedTimeout);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@@ -427,8 +410,7 @@ public abstract class AbstractGemFireIntegrationTests {
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
* @see org.springframework.session.events.AbstractSessionEvent
|
||||
*/
|
||||
public static class SessionEventListener
|
||||
implements ApplicationListener<AbstractSessionEvent> {
|
||||
public static class SessionEventListener implements ApplicationListener<AbstractSessionEvent> {
|
||||
|
||||
private volatile AbstractSessionEvent sessionEvent;
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.session.data.gemfire;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -96,8 +95,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
|
||||
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
|
||||
|
||||
private static final DateFormat TIMESTAMP = new SimpleDateFormat(
|
||||
"yyyy-MM-dd-HH-mm-ss");
|
||||
private static final DateFormat TIMESTAMP = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
||||
|
||||
private static File processWorkingDirectory;
|
||||
|
||||
@@ -114,25 +112,22 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
|
||||
final int port = SocketUtils.findAvailableTcpPort();
|
||||
|
||||
System.err.printf(
|
||||
"Starting GemFire Server running on [%1$s] listening on port [%2$d]%n",
|
||||
InetAddress.getLocalHost().getHostName(), port);
|
||||
System.err.printf("Starting a GemFire Server on [%1$s] listening on port [%2$d]%n",
|
||||
SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port);
|
||||
|
||||
System.setProperty("spring.session.data.gemfire.port", String.valueOf(port));
|
||||
|
||||
String processWorkingDirectoryPathname = String
|
||||
.format("gemfire-client-server-tests-%1$s", TIMESTAMP.format(new Date()));
|
||||
String processWorkingDirectoryPathname = String.format("gemfire-client-server-tests-%1$s",
|
||||
TIMESTAMP.format(new Date()));
|
||||
|
||||
processWorkingDirectory = createDirectory(processWorkingDirectoryPathname);
|
||||
gemfireServer = run(SpringSessionGemFireServerConfiguration.class,
|
||||
processWorkingDirectory,
|
||||
String.format("-Dspring.session.data.gemfire.port=%1$d", port));
|
||||
|
||||
assertThat(waitForCacheServerToStart(
|
||||
SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port)).isTrue();
|
||||
gemfireServer = run(SpringSessionGemFireServerConfiguration.class, processWorkingDirectory,
|
||||
String.format("-Dspring.session.data.gemfire.port=%1$d", port));
|
||||
|
||||
System.err.printf("GemFire Server [startup time = %1$d ms]%n",
|
||||
System.currentTimeMillis() - t0);
|
||||
assertThat(waitForCacheServerToStart(SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port)).isTrue();
|
||||
|
||||
System.err.printf("GemFire Server [startup time = %1$d ms]%n", System.currentTimeMillis() - t0);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@@ -140,11 +135,10 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
if (gemfireServer != null) {
|
||||
gemfireServer.destroyForcibly();
|
||||
System.err.printf("GemFire Server [exit code = %1$d]%n",
|
||||
waitForProcessToStop(gemfireServer, processWorkingDirectory));
|
||||
waitForProcessToStop(gemfireServer, processWorkingDirectory));
|
||||
}
|
||||
|
||||
if (Boolean.valueOf(System.getProperty("spring.session.data.gemfire.fork.clean",
|
||||
Boolean.TRUE.toString()))) {
|
||||
if (Boolean.valueOf(System.getProperty("spring.session.data.gemfire.fork.clean", Boolean.TRUE.toString()))) {
|
||||
FileSystemUtils.deleteRecursively(processWorkingDirectory);
|
||||
}
|
||||
|
||||
@@ -164,8 +158,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
springSessionGemFireRegion.getAttributes();
|
||||
|
||||
assertThat(springSessionGemFireRegionAttributes).isNotNull();
|
||||
assertThat(springSessionGemFireRegionAttributes.getDataPolicy())
|
||||
.isEqualTo(DataPolicy.EMPTY);
|
||||
assertThat(springSessionGemFireRegionAttributes.getDataPolicy()).isEqualTo(DataPolicy.EMPTY);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -179,8 +172,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
|
||||
ExpiringSession expectedSession = save(createSession());
|
||||
|
||||
AbstractSessionEvent sessionEvent = this.sessionEventListener
|
||||
.waitForSessionEvent(500);
|
||||
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
|
||||
|
||||
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
|
||||
|
||||
@@ -188,12 +180,9 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
|
||||
assertThat(createdSession).isEqualTo(expectedSession);
|
||||
assertThat(createdSession.getId()).isNotNull();
|
||||
assertThat(createdSession.getCreationTime())
|
||||
.isGreaterThanOrEqualTo(beforeOrAtCreationTime);
|
||||
assertThat(createdSession.getLastAccessedTime())
|
||||
.isEqualTo(createdSession.getCreationTime());
|
||||
assertThat(createdSession.getMaxInactiveIntervalInSeconds())
|
||||
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
assertThat(createdSession.getCreationTime()).isGreaterThanOrEqualTo(beforeOrAtCreationTime);
|
||||
assertThat(createdSession.getLastAccessedTime()).isEqualTo(createdSession.getCreationTime());
|
||||
assertThat(createdSession.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
|
||||
this.gemfireSessionRepository.delete(expectedSession.getId());
|
||||
}
|
||||
@@ -202,15 +191,13 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
public void getExistingNonExpiredSessionBeforeAndAfterExpiration() {
|
||||
ExpiringSession expectedSession = save(touch(createSession()));
|
||||
|
||||
AbstractSessionEvent sessionEvent = this.sessionEventListener
|
||||
.waitForSessionEvent(500);
|
||||
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
|
||||
|
||||
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
|
||||
assertThat(sessionEvent.<ExpiringSession>getSession()).isEqualTo(expectedSession);
|
||||
assertThat(this.sessionEventListener.getSessionEvent()).isNull();
|
||||
|
||||
ExpiringSession savedSession = this.gemfireSessionRepository
|
||||
.getSession(expectedSession.getId());
|
||||
ExpiringSession savedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
|
||||
|
||||
assertThat(savedSession).isEqualTo(expectedSession);
|
||||
|
||||
@@ -221,13 +208,12 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
this.sessionEventListener.getSessionEvent();
|
||||
|
||||
sessionEvent = this.sessionEventListener.waitForSessionEvent(
|
||||
TimeUnit.SECONDS.toMillis(MAX_INACTIVE_INTERVAL_IN_SECONDS + 1));
|
||||
TimeUnit.SECONDS.toMillis(MAX_INACTIVE_INTERVAL_IN_SECONDS + 1));
|
||||
|
||||
assertThat(sessionEvent).isInstanceOf(SessionExpiredEvent.class);
|
||||
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSession.getId());
|
||||
|
||||
ExpiringSession expiredSession = this.gemfireSessionRepository
|
||||
.getSession(expectedSession.getId());
|
||||
ExpiringSession expiredSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
|
||||
|
||||
assertThat(expiredSession).isNull();
|
||||
}
|
||||
@@ -236,8 +222,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
public void deleteExistingNonExpiredSessionFiresSessionDeletedEventAndReturnsNullOnGet() {
|
||||
ExpiringSession expectedSession = save(touch(createSession()));
|
||||
|
||||
AbstractSessionEvent sessionEvent = this.sessionEventListener
|
||||
.waitForSessionEvent(500);
|
||||
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
|
||||
|
||||
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
|
||||
assertThat(sessionEvent.<ExpiringSession>getSession()).isEqualTo(expectedSession);
|
||||
@@ -249,13 +234,13 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
assertThat(sessionEvent).isInstanceOf(SessionDeletedEvent.class);
|
||||
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSession.getId());
|
||||
|
||||
ExpiringSession deletedSession = this.gemfireSessionRepository
|
||||
.getSession(expectedSession.getId());
|
||||
ExpiringSession deletedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
|
||||
|
||||
assertThat(deletedSession).isNull();
|
||||
}
|
||||
|
||||
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME, maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
|
||||
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME,
|
||||
maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
|
||||
static class SpringSessionGemFireClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -283,6 +268,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
@Bean
|
||||
PoolFactoryBean gemfirePool(@Value("${spring.session.data.gemfire.port:"
|
||||
+ DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
|
||||
|
||||
PoolFactoryBean poolFactory = new PoolFactoryBean();
|
||||
|
||||
poolFactory.setFreeConnectionTimeout(5000); // 5 seconds
|
||||
@@ -290,7 +276,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
poolFactory.setMaxConnections(SpringSessionGemFireServerConfiguration.MAX_CONNECTIONS);
|
||||
poolFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
|
||||
poolFactory.setReadTimeout(2000); // 2 seconds
|
||||
poolFactory.setRetryAttempts(2);
|
||||
poolFactory.setRetryAttempts(1);
|
||||
poolFactory.setSubscriptionEnabled(true);
|
||||
poolFactory.setThreadLocalConnections(false);
|
||||
|
||||
@@ -337,8 +323,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
Properties gemfireProperties() {
|
||||
Properties gemfireProperties = new Properties();
|
||||
|
||||
gemfireProperties.setProperty("name",
|
||||
SpringSessionGemFireServerConfiguration.class.getName());
|
||||
gemfireProperties.setProperty("name", name());
|
||||
gemfireProperties.setProperty("mcast-port", "0");
|
||||
gemfireProperties.setProperty("log-file", "server.log");
|
||||
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
|
||||
@@ -346,6 +331,10 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
String name() {
|
||||
return SpringSessionGemFireServerConfiguration.class.getName();
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheFactoryBean gemfireCache() {
|
||||
CacheFactoryBean gemfireCache = new CacheFactoryBean();
|
||||
@@ -358,8 +347,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
|
||||
@Bean
|
||||
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache,
|
||||
@Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT
|
||||
+ "}") int port) {
|
||||
@Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
|
||||
|
||||
CacheServerFactoryBean cacheServerFactory = new CacheServerFactoryBean();
|
||||
|
||||
@@ -375,7 +363,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
@SuppressWarnings("resource")
|
||||
public static void main(final String[] args) throws IOException {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
SpringSessionGemFireServerConfiguration.class);
|
||||
SpringSessionGemFireServerConfiguration.class);
|
||||
context.registerShutdownHook();
|
||||
writeProcessControlFile(WORKING_DIRECTORY);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.gemfire;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
import com.gemstone.gemfire.cache.DataPolicy;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
import com.gemstone.gemfire.cache.RegionAttributes;
|
||||
import com.gemstone.gemfire.cache.client.ClientCache;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.data.gemfire.CacheFactoryBean;
|
||||
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
|
||||
import org.springframework.data.gemfire.client.PoolFactoryBean;
|
||||
import org.springframework.data.gemfire.server.CacheServerFactoryBean;
|
||||
import org.springframework.data.gemfire.support.ConnectionEndpoint;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
|
||||
import org.springframework.session.data.gemfire.support.GemFireUtils;
|
||||
import org.springframework.session.events.AbstractSessionEvent;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionExpiredEvent;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* The MultiPoolClientServerGemFireOperationsSessionRepositoryIntegrationTests class is a test suite of test cases
|
||||
* testing the functionality of a GemFire cache client in a Spring Session application using a specifically named
|
||||
* GemFire {@link com.gemstone.gemfire.cache.client.Pool} as configured with the 'poolName' attribute on the
|
||||
* Spring Session Data GemFire {@link EnableGemFireHttpSession} annotation.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.junit.runner.RunWith
|
||||
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
|
||||
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
|
||||
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
|
||||
* @see org.springframework.test.annotation.DirtiesContext
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
|
||||
* @see org.springframework.test.context.web.WebAppConfiguration
|
||||
* @see com.gemstone.gemfire.cache.Cache
|
||||
* @see com.gemstone.gemfire.cache.client.ClientCache
|
||||
* @see com.gemstone.gemfire.cache.client.Pool
|
||||
* @see com.gemstone.gemfire.cache.server.CacheServer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = MultiPoolClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
.SpringSessionGemFireClientConfiguration.class)
|
||||
@DirtiesContext
|
||||
@WebAppConfiguration
|
||||
public class MultiPoolClientServerGemFireOperationsSessionRepositoryIntegrationTests
|
||||
extends AbstractGemFireIntegrationTests {
|
||||
|
||||
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
|
||||
|
||||
private static final DateFormat TIMESTAMP = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
||||
|
||||
private static File processWorkingDirectory;
|
||||
|
||||
private static Process gemfireServer;
|
||||
|
||||
private static final String SPRING_SESSION_GEMFIRE_REGION_NAME = "TestMultiPoolClientServerSessions";
|
||||
|
||||
@Autowired
|
||||
private SessionEventListener sessionEventListener;
|
||||
|
||||
@BeforeClass
|
||||
public static void startGemFireServer() throws IOException {
|
||||
final long t0 = System.currentTimeMillis();
|
||||
|
||||
final int port = SocketUtils.findAvailableTcpPort();
|
||||
|
||||
System.err.printf("Starting a GemFire Server on [%1$s] listening on port [%2$d]%n",
|
||||
SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port);
|
||||
|
||||
System.setProperty("spring.session.data.gemfire.port", String.valueOf(port));
|
||||
|
||||
String processWorkingDirectoryPathname = String.format("gemfire-multipool-client-server-tests-%1$s",
|
||||
TIMESTAMP.format(new Date()));
|
||||
|
||||
processWorkingDirectory = createDirectory(processWorkingDirectoryPathname);
|
||||
|
||||
gemfireServer = run(SpringSessionGemFireServerConfiguration.class, processWorkingDirectory,
|
||||
String.format("-Dspring.session.data.gemfire.port=%1$d", port));
|
||||
|
||||
assertThat(waitForCacheServerToStart(SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port)).isTrue();
|
||||
|
||||
System.err.printf("GemFire Server [startup time = %1$d ms]%n", System.currentTimeMillis() - t0);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopGemFireServerAndDeleteArtifacts() {
|
||||
if (gemfireServer != null) {
|
||||
gemfireServer.destroyForcibly();
|
||||
System.err.printf("GemFire Server [exit code = %1$d]%n",
|
||||
waitForProcessToStop(gemfireServer, processWorkingDirectory));
|
||||
}
|
||||
|
||||
if (Boolean.valueOf(System.getProperty("spring.session.data.gemfire.fork.clean", Boolean.TRUE.toString()))) {
|
||||
FileSystemUtils.deleteRecursively(processWorkingDirectory);
|
||||
}
|
||||
|
||||
assertThat(waitForClientCacheToClose(DEFAULT_WAIT_DURATION)).isTrue();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
assertThat(GemFireUtils.isClient(gemfireCache)).isTrue();
|
||||
|
||||
Region<Object, ExpiringSession> springSessionGemFireRegion =
|
||||
gemfireCache.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
|
||||
|
||||
assertThat(springSessionGemFireRegion).isNotNull();
|
||||
|
||||
RegionAttributes<Object, ExpiringSession> springSessionGemFireRegionAttributes =
|
||||
springSessionGemFireRegion.getAttributes();
|
||||
|
||||
assertThat(springSessionGemFireRegionAttributes).isNotNull();
|
||||
assertThat(springSessionGemFireRegionAttributes.getDataPolicy()).isEqualTo(DataPolicy.EMPTY);
|
||||
}
|
||||
|
||||
protected static ConnectionEndpoint newConnectionEndpoint(String host, int port) {
|
||||
return new ConnectionEndpoint(host, port);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getExistingNonExpiredSessionBeforeAndAfterExpiration() {
|
||||
ExpiringSession expectedSession = save(touch(createSession()));
|
||||
|
||||
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
|
||||
|
||||
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
|
||||
assertThat(sessionEvent.<ExpiringSession>getSession()).isEqualTo(expectedSession);
|
||||
assertThat(this.sessionEventListener.getSessionEvent()).isNull();
|
||||
|
||||
ExpiringSession savedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
|
||||
|
||||
assertThat(savedSession).isEqualTo(expectedSession);
|
||||
|
||||
this.sessionEventListener.getSessionEvent();
|
||||
|
||||
sessionEvent = this.sessionEventListener.waitForSessionEvent(
|
||||
TimeUnit.SECONDS.toMillis(MAX_INACTIVE_INTERVAL_IN_SECONDS + 1));
|
||||
|
||||
assertThat(sessionEvent).isInstanceOf(SessionExpiredEvent.class);
|
||||
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSession.getId());
|
||||
|
||||
ExpiringSession expiredSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
|
||||
|
||||
assertThat(expiredSession).isNull();
|
||||
}
|
||||
|
||||
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME, poolName = "serverPool",
|
||||
maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
|
||||
static class SpringSessionGemFireClientConfiguration {
|
||||
|
||||
@Bean
|
||||
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Properties gemfireProperties() {
|
||||
Properties gemfireProperties = new Properties();
|
||||
gemfireProperties.setProperty("name", name());
|
||||
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
String name() {
|
||||
return SpringSessionGemFireClientConfiguration.class.getName();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ClientCacheFactoryBean gemfireCache() {
|
||||
ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();
|
||||
|
||||
gemfireCache.setClose(true);
|
||||
gemfireCache.setPoolName("gemfirePool");
|
||||
gemfireCache.setProperties(gemfireProperties());
|
||||
|
||||
return gemfireCache;
|
||||
}
|
||||
|
||||
@Bean
|
||||
PoolFactoryBean gemfirePool() {
|
||||
PoolFactoryBean poolFactory = new PoolFactoryBean();
|
||||
|
||||
poolFactory.setFreeConnectionTimeout(5000); // 5 seconds
|
||||
poolFactory.setKeepAlive(false);
|
||||
poolFactory.setMinConnections(0);
|
||||
poolFactory.setReadTimeout(500);
|
||||
|
||||
// deliberately set to a non-existing GemFire (Cache) Server
|
||||
poolFactory.addServers(newConnectionEndpoint("localhost", 53135));
|
||||
|
||||
return poolFactory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
PoolFactoryBean serverPool(@Value("${spring.session.data.gemfire.port:"
|
||||
+ DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
|
||||
|
||||
PoolFactoryBean poolFactory = new PoolFactoryBean();
|
||||
|
||||
poolFactory.setFreeConnectionTimeout(5000); // 5 seconds
|
||||
poolFactory.setKeepAlive(false);
|
||||
poolFactory.setMaxConnections(SpringSessionGemFireServerConfiguration.MAX_CONNECTIONS);
|
||||
poolFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
|
||||
poolFactory.setReadTimeout(2000); // 2 seconds
|
||||
poolFactory.setRetryAttempts(1);
|
||||
poolFactory.setSubscriptionEnabled(true);
|
||||
poolFactory.setThreadLocalConnections(false);
|
||||
|
||||
poolFactory.addServers(newConnectionEndpoint(
|
||||
SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port));
|
||||
|
||||
return poolFactory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AbstractGemFireIntegrationTests.SessionEventListener sessionEventListener() {
|
||||
return new AbstractGemFireIntegrationTests.SessionEventListener();
|
||||
}
|
||||
|
||||
// used for debugging purposes
|
||||
@SuppressWarnings("resource")
|
||||
public static void main(final String[] args) {
|
||||
ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(
|
||||
SpringSessionGemFireClientConfiguration.class);
|
||||
|
||||
applicationContext.registerShutdownHook();
|
||||
|
||||
ClientCache clientCache = applicationContext.getBean(ClientCache.class);
|
||||
|
||||
for (InetSocketAddress server : clientCache.getCurrentServers()) {
|
||||
System.err.printf("GemFire Server [host: %1$s, port: %2$d]%n",
|
||||
server.getHostName(), server.getPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME, maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
|
||||
static class SpringSessionGemFireServerConfiguration {
|
||||
|
||||
static final int MAX_CONNECTIONS = 50;
|
||||
static final String SERVER_HOSTNAME = "localhost";
|
||||
|
||||
@Bean
|
||||
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Properties gemfireProperties() {
|
||||
Properties gemfireProperties = new Properties();
|
||||
|
||||
gemfireProperties.setProperty("name", name());
|
||||
gemfireProperties.setProperty("mcast-port", "0");
|
||||
gemfireProperties.setProperty("log-file", "server.log");
|
||||
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
|
||||
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
String name() {
|
||||
return SpringSessionGemFireServerConfiguration.class.getName();
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheFactoryBean gemfireCache() {
|
||||
CacheFactoryBean gemfireCache = new CacheFactoryBean();
|
||||
|
||||
gemfireCache.setClose(true);
|
||||
gemfireCache.setProperties(gemfireProperties());
|
||||
|
||||
return gemfireCache;
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache,
|
||||
@Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
|
||||
|
||||
CacheServerFactoryBean cacheServerFactory = new CacheServerFactoryBean();
|
||||
|
||||
cacheServerFactory.setAutoStartup(true);
|
||||
cacheServerFactory.setBindAddress(SERVER_HOSTNAME);
|
||||
cacheServerFactory.setCache(gemfireCache);
|
||||
cacheServerFactory.setMaxConnections(MAX_CONNECTIONS);
|
||||
cacheServerFactory.setPort(port);
|
||||
|
||||
return cacheServerFactory;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public static void main(final String[] args) throws IOException {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
SpringSessionGemFireServerConfiguration.class);
|
||||
context.registerShutdownHook();
|
||||
writeProcessControlFile(WORKING_DIRECTORY);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.session.jdbc;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -27,11 +28,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
@@ -51,7 +48,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link JdbcOperationsSessionRepository}.
|
||||
* Abstract base class for {@link JdbcOperationsSessionRepository} integration tests.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 1.2.0
|
||||
@@ -59,7 +56,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class JdbcOperationsSessionRepositoryITests {
|
||||
public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
@@ -505,9 +502,7 @@ public class JdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanupCleansUpInactiveSessions() {
|
||||
this.repository.setDefaultMaxInactiveInterval(1800); // 30 minutes
|
||||
|
||||
public void cleanupInactiveSessionsUsingRepositoryDefinedInterval() {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
|
||||
@@ -517,32 +512,50 @@ public class JdbcOperationsSessionRepositoryITests {
|
||||
|
||||
this.repository.cleanUpExpiredSessions();
|
||||
|
||||
// verify its not cleaned up immediately after creation
|
||||
assertThat(this.repository.getSession(session.getId())).isNotNull();
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
long tenMinutesInMilliseconds = 1000 * 60 * 10;
|
||||
long thirtyMinutesInMilliseconds = 1000 * 60 * 30;
|
||||
|
||||
// set the last accessed time to 10 minutes in the past, session should not get
|
||||
// cleaned up because it has not been inactive for 30 minutes yet
|
||||
long tenMinutesInThePast = now - tenMinutesInMilliseconds;
|
||||
session.setLastAccessedTime(tenMinutesInThePast);
|
||||
session.setLastAccessedTime(now - TimeUnit.MINUTES.toMillis(10));
|
||||
this.repository.save(session);
|
||||
|
||||
this.repository.cleanUpExpiredSessions();
|
||||
|
||||
// session should still exist
|
||||
assertThat(this.repository.getSession(session.getId())).isNotNull();
|
||||
|
||||
// set the last accessed time to 30 minutes in the past, session should get
|
||||
// cleaned up as it has been inactive for 30 minutes
|
||||
long thirtyMinutesInThePast = now - thirtyMinutesInMilliseconds;
|
||||
session.setLastAccessedTime(thirtyMinutesInThePast);
|
||||
session.setLastAccessedTime(now - TimeUnit.MINUTES.toMillis(30));
|
||||
this.repository.save(session);
|
||||
this.repository.cleanUpExpiredSessions();
|
||||
|
||||
assertThat(this.repository.getSession(session.getId())).isNull();
|
||||
}
|
||||
|
||||
// gh-580
|
||||
@Test
|
||||
public void cleanupInactiveSessionsUsingSessionDefinedInterval() {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
session.setMaxInactiveIntervalInSeconds((int) TimeUnit.MINUTES.toSeconds(45));
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(this.repository.getSession(session.getId())).isNotNull();
|
||||
|
||||
this.repository.cleanUpExpiredSessions();
|
||||
// session should have been cleaned up
|
||||
|
||||
assertThat(this.repository.getSession(session.getId())).isNotNull();
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
session.setLastAccessedTime(now - TimeUnit.MINUTES.toMillis(40));
|
||||
this.repository.save(session);
|
||||
this.repository.cleanUpExpiredSessions();
|
||||
|
||||
assertThat(this.repository.getSession(session.getId())).isNotNull();
|
||||
|
||||
session.setLastAccessedTime(now - TimeUnit.MINUTES.toMillis(50));
|
||||
this.repository.save(session);
|
||||
this.repository.cleanUpExpiredSessions();
|
||||
|
||||
assertThat(this.repository.getSession(session.getId())).isNull();
|
||||
}
|
||||
|
||||
@@ -554,15 +567,8 @@ public class JdbcOperationsSessionRepositoryITests {
|
||||
return this.changedContext.getAuthentication().getName();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableJdbcHttpSession
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public EmbeddedDatabase dataSource() {
|
||||
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
|
||||
.addScript("org/springframework/session/jdbc/schema-h2.sql").build();
|
||||
}
|
||||
protected static class BaseConfig {
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager(DataSource dataSource) {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user