Compare commits

...

33 Commits

Author SHA1 Message Date
Spring Buildmaster
245e634bea Release version 1.3.0.M2 2016-09-14 19:08:14 +00:00
Rob Winch
7a2914323f Update to Hazelcast 3.6.5
Issue gh-544
2016-09-14 11:32:03 -05:00
Vedran Pavic
6e04d903ae Add HazelcastSessionRepository
This commit improves existing Hazelcast support, which is based on
MapSessionRepository, with dedicated HazelcastSessionRepository
that implements the FindByIndexNameSessionRepository contract.

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

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

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

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

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

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

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

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

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

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

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

 add SpringSessionBackedSessionRegistry

*  Spring Security Concurrent Session Integration #65

 add documentation

*  Spring Security Concurrent Session Integration #65

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

This commit improves result set extraction by using a `ResultSetExtractor` which does not require scrollable result sets.
2016-08-15 13:34:12 -05:00
Vedran Pavić
9133e337e6 Use webjars-locator in samples (#587) 2016-08-15 13:25:53 -05:00
Rob Winch
9f36fd69ee Version is 1.3.0.BUILD-SNAPSHOT 2016-06-29 22:52:40 -05:00
Spring Buildmaster
e684f58403 Next development version 2016-06-30 03:05:34 +00:00
150 changed files with 13486 additions and 944 deletions

View File

@@ -9,10 +9,6 @@ jdk:
os:
- linux
branches:
only:
- master
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
cache:

View File

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

View File

@@ -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',

View File

@@ -64,6 +64,8 @@ Ensure you have the following in your pom.xml:
----
endif::[]
// tag::config[]
[[security-spring-configuration]]
== Spring Configuration
@@ -79,7 +81,9 @@ include::{docs-test-dir}docs/http/HazelcastHttpSessionConfig.java[tags=config]
<1> The `@EnableHazelcastHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
In this instance Spring Session is backed by Hazelcast.
<2> We create a `HazelcastInstance` that connects Spring Session to Hazelcast.
<2> In order to support retrieval of sessions by principal name index, appropriate `ValueExtractor` needs to be registered.
Spring Session provides `PrincipalNameExtractor` for this purpose.
<3> We create a `HazelcastInstance` that connects Spring Session to Hazelcast.
By default, an embedded instance of Hazelcast is started and connected to by the application.
For more information on configuring Hazelcast, refer to the http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[reference documentation].
@@ -88,8 +92,8 @@ For more information on configuring Hazelcast, refer to the http://docs.hazelcas
Our <<security-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
In order for our `Filter` to do its magic, Spring needs to load our `Config` class.
Since our application is already loading Spring configuration using our `SecurityInitializer` class, we can simply add our Config class to it.
In order for our `Filter` to do its magic, Spring needs to load our `SessionConfig` class.
Since our application is already loading Spring configuration using our `SecurityInitializer` class, we can simply add our `SessionConfig` class to it.
.src/main/java/sample/SecurityInitializer.java
[source,java]
@@ -113,7 +117,7 @@ NOTE: The name of our class (Initializer) does not matter. What is important is
By extending `AbstractHttpSessionApplicationInitializer` we ensure that the Spring Bean by the name `springSessionRepositoryFilter` is registered with our Servlet Container for every request before Spring Security's `springSecurityFilterChain`.
// end::config[]
[[hazelcast-spring-security-sample]]
== Hazelcast Spring Security Sample Application
@@ -188,4 +192,4 @@ For example, you could delete an individual key as follows (replacing `7e8383a4-
TIP: The port number of the Hazelcast node will be printed to the console on startup. Replace `xxxxx` above with the port number.
Now observe that you are no longer authenticated with this session.
Now observe that you are no longer authenticated with this session.

View File

@@ -368,6 +368,18 @@ There is also a constructor taking `Serializer` and `Deserializer` objects, allo
You can create your own session converter by extending `AbstractMongoSessionConverter` class.
The implementation will be used for serializing, deserializing your objects and for providing queries to access the session.
[[httpsession-hazelcast]]
=== HttpSession with Hazelcast
Using Spring Session with `HttpSession` is enabled by adding a Servlet Filter before anything that uses the `HttpSession`.
This section describes how to use Hazelcast to back `HttpSession` using Java based configuration.
NOTE: The <<samples, Hazelcast Spring Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using Java configuration.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed Hazelcast Spring Guide when integrating with your own application.
include::guides/hazelcast-spring.adoc[tags=config,leveloffset=+2]
[[httpsession-how]]
=== How HttpSession Integration Works
@@ -505,6 +517,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
@@ -610,44 +659,6 @@ It is important to note that no infrastructure for session expirations is config
This is because things like session expiration are highly implementation dependent.
This means if you require cleaning up expired sessions, you are responsible for cleaning up the expired sessions.
[[api-enablehazelcasthttpsession]]
=== EnableHazelcastHttpSession
If you wish to use http://hazelcast.org/[Hazelcast] as your backing source for the `SessionRepository`, then the `@EnableHazelcastHttpSession` annotation
can be added to an `@Configuration` class. This extends the functionality provided by the `@EnableSpringHttpSession` annotation but makes the `SessionRepository` for you in Hazelcast.
You must provide a single `HazelcastInstance` bean for the configuration to work.
For example:
[source,java,indent=0]
----
include::{docs-test-dir}docs/http/HazelcastHttpSessionConfig.java[tags=config]
----
This will configure Hazelcast in embedded mode with default configuration.
See the http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[Hazelcast documentation] for
detailed information on configuration options for Hazelcast.
[[api-enablehazelcasthttpsession-storage]]
==== Storage Details
Sessions will be stored in a distributed `Map` in Hazelcast using a <<api-mapsessionrepository,MapSessionRepository>>.
The `Map` interface methods will be used to `get()` and `put()` Sessions.
The expiration of a session in the `Map` is handled by Hazelcast's support for setting the time to live on an entry when it is `put()` into the `Map`. Entries (sessions) that have been idle longer than the time to live will be automatically removed from the `Map`.
You shouldn't need to configure any settings such as `max-idle-seconds` or `time-to-live-seconds` for the `Map` within the Hazelcast configuration.
[[api-enablehazelcasthttpsession-customize]]
==== Basic Customization
You can use the following attributes on `@EnableHazelcastHttpSession` to customize the configuration:
* **maxInactiveIntervalInSeconds** - the amount of time before the session will expire in seconds. Default is 1800 seconds (30 minutes)
* **sessionMapName** - the name of the distributed `Map` that will be used in Hazelcast to store the session data.
[[api-enablehazelcasthttpsession-events]]
==== Session Events
Using a `MapListener` to respond to entries being added, evicted, and removed from the distributed `Map`, these events will trigger
publishing SessionCreatedEvent, SessionExpiredEvent, and SessionDeletedEvent events respectively using the `ApplicationEventPublisher`.
[[api-redisoperationssessionrepository]]
=== RedisOperationsSessionRepository
@@ -837,7 +848,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 +1054,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.
@@ -1066,6 +1078,54 @@ include::{session-main-resources-dir}org/springframework/session/jdbc/schema-mys
All JDBC operations in `JdbcOperationsSessionRepository` are executed in a transactional manner.
Transactions are executed with propagation set to `REQUIRES_NEW` in order to avoid unexpected behavior due to interference with existing transactions (for example, executing `save` operation in a thread that already participates in a read-only transaction).
[[api-hazelcastsessionrepository]]
=== HazelcastSessionRepository
`HazelcastSessionRepository` is a `SessionRepository` implementation that stores sessions in Hazelcast's distributed `IMap`.
In a web environment, this is typically used in combination with `SessionRepositoryFilter`.
[[api-hazelcastsessionrepository-new]]
==== Instantiating a HazelcastSessionRepository
A typical example of how to create a new instance can be seen below:
[source,java,indent=0]
----
include::{indexdoc-tests}[tags=new-hazelcastsessionrepository]
----
For additional information on how to create and configure Hazelcast instance, refer to the http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[Hazelcast documentation].
[[api-enablehazelcasthttpsession]]
==== EnableHazelcastHttpSession
If you wish to use http://hazelcast.org/[Hazelcast] as your backing source for the `SessionRepository`, then the `@EnableHazelcastHttpSession` annotation
can be added to an `@Configuration` class. This extends the functionality provided by the `@EnableSpringHttpSession` annotation but makes the `SessionRepository` for you in Hazelcast.
You must provide a single `HazelcastInstance` bean for the configuration to work.
Complete configuration example can be found in the <<samples>>
[[api-enablehazelcasthttpsession-storage]]
==== Storage Details
Sessions will be stored in a distributed `IMap` in Hazelcast using a <<api-mapsessionrepository,MapSessionRepository>>.
The `IMap` interface methods will be used to `get()` and `put()` Sessions.
Additionally, `values()` method is used to support `FindByIndexNameSessionRepository#findByIndexNameAndIndexValue` operation, together with appropriate `ValueExtractor` that needs to be registered with Hazelcast. Refer to <<samples, Hazelcast Spring Sample>> for more details on this configuration.
The expiration of a session in the `IMap` is handled by Hazelcast's support for setting the time to live on an entry when it is `put()` into the `IMap`. Entries (sessions) that have been idle longer than the time to live will be automatically removed from the `IMap`.
You shouldn't need to configure any settings such as `max-idle-seconds` or `time-to-live-seconds` for the `IMap` within the Hazelcast configuration.
[[api-enablehazelcasthttpsession-customize]]
==== Basic Customization
You can use the following attributes on `@EnableHazelcastHttpSession` to customize the configuration:
* **maxInactiveIntervalInSeconds** - the amount of time before the session will expire in seconds. Default is 1800 seconds (30 minutes)
* **sessionMapName** - the name of the distributed `Map` that will be used in Hazelcast to store the session data.
[[api-enablehazelcasthttpsession-events]]
==== Session Events
Using a `MapListener` to respond to entries being added, evicted, and removed from the distributed `Map`, these events will trigger
publishing SessionCreatedEvent, SessionExpiredEvent, and SessionDeletedEvent events respectively using the `ApplicationEventPublisher`.
[[community]]
== Spring Session Community

View File

@@ -16,6 +16,10 @@
package docs;
import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import org.junit.Test;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
@@ -23,10 +27,12 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.mock.web.MockServletContext;
import org.springframework.session.ExpiringSession;
import org.springframework.session.MapSession;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.hazelcast.HazelcastSessionRepository;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.transaction.PlatformTransactionManager;
@@ -135,6 +141,25 @@ public class IndexDocTests {
// end::new-jdbcoperationssessionrepository[]
}
@Test
@SuppressWarnings("unused")
public void newHazelcastSessionRepository() {
// tag::new-hazelcastsessionrepository[]
Config config = new Config();
// ... configure Hazelcast ...
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);
IMap<String, MapSession> sessions = hazelcastInstance
.getMap("spring:session:sessions");
HazelcastSessionRepository repository =
new HazelcastSessionRepository(sessions);
// end::new-hazelcastsessionrepository[]
}
@Test
public void runSpringHttpSessionConfig() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();

View File

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

View File

@@ -0,0 +1,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[]

View File

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

View File

@@ -4,24 +4,26 @@ jacksonVersion=2.6.5
jspApiVersion=2.0
servletApiVersion=3.0.1
jstlelVersion=1.2.5
version=1.2.1.RELEASE
version=1.3.0.M2
springDataRedisVersion=1.7.1.RELEASE
html5ShivVersion=3.7.3
commonsLoggingVersion=1.2
junitVersion=4.12
gebVersion=0.13.1
mockitoVersion=1.10.19
hazelcastVersion=3.5.4
hazelcastVersion=3.6.5
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

View File

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

View File

@@ -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}"
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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",

View File

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

View File

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

View File

@@ -22,7 +22,7 @@ import org.springframework.security.web.context.AbstractSecurityWebApplicationIn
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityConfig.class, Config.class);
super(SecurityConfig.class, SessionConfig.class);
}
}
// end::class[]

View File

@@ -16,33 +16,53 @@
package sample;
import com.hazelcast.config.NetworkConfig;
import com.hazelcast.config.Config;
import com.hazelcast.config.MapAttributeConfig;
import com.hazelcast.config.MapIndexConfig;
import com.hazelcast.config.SerializerConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.hazelcast.HazelcastSessionRepository;
import org.springframework.session.hazelcast.PrincipalNameExtractor;
import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
import org.springframework.util.SocketUtils;
// tag::class[]
@EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = 300)
@Configuration
public class Config {
public class SessionConfig {
@Bean(destroyMethod = "shutdown")
public HazelcastInstance hazelcastInstance() {
com.hazelcast.config.Config cfg = new com.hazelcast.config.Config();
NetworkConfig netConfig = new NetworkConfig();
netConfig.setPort(SocketUtils.findAvailableTcpPort());
System.out.println("Hazelcast port #: " + netConfig.getPort());
cfg.setNetworkConfig(netConfig);
SerializerConfig serializer = new SerializerConfig().setTypeClass(Object.class)
.setImplementation(new ObjectStreamSerializer());
cfg.getSerializationConfig().addSerializerConfig(serializer);
Config config = new Config();
return Hazelcast.newHazelcastInstance(cfg);
int port = SocketUtils.findAvailableTcpPort();
config.getNetworkConfig()
.setPort(port);
System.out.println("Hazelcast port #: " + port);
SerializerConfig serializer = new SerializerConfig()
.setImplementation(new ObjectStreamSerializer())
.setTypeClass(Object.class);
config.getSerializationConfig()
.addSerializerConfig(serializer);
MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
config.getMapConfig("spring:session:sessions")
.addMapAttributeConfig(attributeConfig)
.addMapIndexConfig(new MapIndexConfig(
HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
return Hazelcast.newHazelcastInstance(config);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,7 +23,6 @@
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
<!-- end::springSessionRepositoryFilter[] -->

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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[] -->

View File

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

View File

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

View File

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

View 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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 {
}

View File

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

View File

@@ -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 {
}

View File

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

View File

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

View File

@@ -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 {
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
spring.thymeleaf.cache=false
spring.template.cache=false
#logging.level.org.springframework.security=DEBUG

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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[] -->

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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",

View File

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

View File

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

View File

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

View File

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

View File

@@ -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",

View File

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

View File

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

View File

@@ -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,8 @@ include 'samples:grails3'
include 'spring-session'
include 'spring-session-data-gemfire'
include 'spring-session-data-redis'
include 'spring-session-jdbc'
include 'spring-session-data-geode'
include 'spring-session-data-mongo'
include 'spring-session-data-redis'
include 'spring-session-hazelcast'
include 'spring-session-jdbc'

View 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}"
}
}
}

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