2. @dgomezg
Agenda
• Session Data: State of the art
• Spring-‐Session: Motivations & Goals
• Out of the box integration with Redis
• Use Cases
• Change persistent store
• Multiple user login handling
• Adding session to RESTful APIs
4. @dgomezg
Session Data: State of the art.
• HttpSession as data container
• Retrieved from the HttpServletRequest
• Implementation dependent on servlet
container
• Persistency configurable on servlet
container
8. @dgomezg
Spring Session Goals
• Provide a mechanism to store & configure
session data handling
• Platform independent
• Transparent to standard HttpSession usage
• Easily configurable
• Easily extensible
• New persistence mechanisms
10. @dgomezg
How it works
Session is accessed via HttpRequest
@RequestMapping("/user/session")
public String addToSession(HttpServletRequest request,
@RequestParam("attr") String attribute,
@RequestParam("val") String value) {
HttpSession session = request.getSession();
session.setAttribute(attribute, value);
return "redirect:/";
}
The Goal: intercept the Session access to
replace the Session creation by a richer implementation
12. @dgomezg
SessionRepositoryRequestWrapper
Overrides methods that return an HttpSession
•Encapsulates creation of specific HttpSession
•Handles HttpSession persistence (if any)
private final class SessionRepositoryRequestWrapper
extends HttpServletRequestWrapper {
@Override
public HttpSession getSession(boolean create) {/* Impl omitted */}
@Override
public HttpSession getSession() {/* Impl omitted */}
}
13. @dgomezg
HttpSessionWrapper
Specific HttpSession implementation
•Transparent interface for the Application
•Independent from Servlet Container
•Spring-‐Session and Spring-‐Repository aware
private final class HttpSessionWrapper implements HttpSession {
}
14. @dgomezg
SessionRepositoryFilter
Bootstraps Spring Session architecture
Should be placed first in the Filter Chain
Bean name is mandatory:
• springSessionRepositoryFilter
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
15. @dgomezg
SessionRepositoryFilter
Bootstraps Spring Session architecture
Servlet 3.0:
Use AbstractHttpSessionApplicationInitializer
public class Initializer
extends AbstractHttpSessionApplicationInitializer {
public Initializer() {
super(Config.class);
}
}
18. @dgomezg
Components needed
• SpringSessionFilter (already implemented)
• A SessionRepository implementation
• A redis backed implementation out-‐of-‐the-‐
box
• A persistence service (optional)
• (i.e) an external redis service
20. @dgomezg
Step 2: Import Redis Configuration
<context:annotation-config/>
<bean
class="org.springframework.session.data.redis.config.annotation.web.ht
tp.RedisHttpSessionConfiguration"/>
RedisHttpSessionConfiguration configures:
• SessionRepositoryFilter
• A SessionRepository that persists to redis
• needs a RedisConnectionFactory
spring/session.xml
21. @dgomezg
Step 2: Import Redis Configuration
@EnableRedisHttpSession
//@Import(RedisHttpSessionConfiguration.class)
public class Config {
}
RedisHttpSessionConfiguration configures:
• SessionRepositoryFilter
• A SessionRepository that persists to redis
• needs a RedisConnectionFactory
22. @dgomezg
Step 3: RedisConnectionFactory
<bean id="connectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:hostName="${redis.host}" p:port="${redis.port}"
p:password="${redis.pass}"/>
<context:property-placeholder location="classpath:redis.properties"/>
Using Jedis client
• Defined in spring-‐data-‐redis.jar
Configures the external connection to Redis server
spring/redis.xml
23. @dgomezg
Step 3: RedisConnectionFactory
@Bean
public JedisConnectionFactory connectionFactory(
@Value("${spring.redis.host") String host,
@Value(“${spring.redis.port}”) int port,
@Value("${spring.redis.pass}") String pass) {
JedisConnectionFactory connection = new JedisConnectionFactory();
connection.setHostName(host);
connection.setPort(port);
connection.setPassword(pass);
return connection;
}
Using Jedis client
• Defined in spring-‐data-‐redis.jar
Configures the external connection to Redis server
24. @dgomezg
Step 3b: RedisConnectionFactory
@EnableEmbeddedRedis // @Import(EmbeddedRedisConfiguration.class)
@EnableRedisHttpSession
public class Config {
@Bean
public JedisConnectionFactory connectionFactory(
@RedisServerPort int port) {
JedisConnectionFactory connection =
new JedisConnectionFactory();
connection.setPort(port);
return connection;
}
}
An embedded Redis Configuration is available in
spring-‐session-‐samples project
25. @dgomezg
Step 4: Session usage
@Controller
public class SessionDataController {
@RequestMapping("/user/session")
public String addToSession(HttpServletRequest request,
@RequestParam("attr") String attribute,
@RequestParam("val") String value) {
HttpSession session = request.getSession();
session.setAttribute(attribute, value);
return "redirect:/";
}
}
Use your HttpSession as usual
(through HttpServletRequest)
26. @dgomezg
Step 5: Bootstrap SpringSession
public class Initializer
extends AbstractHttpSessionApplicationInitializer {
public Initializer() {
super(Config.class);
}
}
Define a DelegatingFilterProxy with specific name
springSessionRepositoryFilter
30. @dgomezg
Further customization
• Out of the box impl. implies:
• MaxInactiveInterval = 30 min
• Session id exchanged through cookies
(SessionStrategy)
• SessionStore:
RedisOperationsSessionRepository
• RedisSerialization
• All defined in RedisHttpSessionConfiguration
31. @dgomezg
Default inactive time
• Configurable as property in
RedisHttpSessionConfiguration
<context:annotation-config/>
<bean
class=“org.springframework.session.data.redis.config.annotation.web.ht
tp.RedisHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="3600"/>
spring/session.xml
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
public class Config {
}
32. @dgomezg
Session Strategy
Configures the policy to exchange the session Id.
Two implementations:
•CookieSessionStrategy (default)
•HeaderHttpSessionStrategy
<context:annotation-config/>
<bean
class=“org.springframework.session.data.redis.config.annotation.web.ht
tp.RedisHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="3600"/>
spring/session.xml
34. @dgomezg
CookieSessionStrategy
Can be customised to change cookie name
<context:annotation-config/>
<bean id="httpSessionStrategy"
class="org.springframework.session.web.http.CookieHttpSessionStrategy"
p:cookieName="UserSessionID"/>
<bean class=“o.s.s.d.r.c.annotation.web.http.RedisHttpSessionConfiguration"
p:httpSessionStrategy-ref=“httpSessionStrategy”/>
spring/session.xml
35. @dgomezg
HeaderHttpSessionStrategy
Session id is specified on request headers
• x-‐auth-‐token by default
• header name can be modified
<context:annotation-config/>
<bean id="httpSessionStrategy"
class="org.springframework.session.web.http.HeaderHttpSessionStrategy"
p:headerName=“x-auth-token“/>
<bean class=“o.s.s.d.r.c.annotation.web.http.RedisHttpSessionConfiguration"
p:httpSessionStrategy-ref=“httpSessionStrategy”/>
spring/session.xml
36. @dgomezg
Your own Strategy
Other strategies can be implemented & plugged
Implement HttpSessionStrategy
public interface HttpSessionStrategy {
String getRequestedSessionId(HttpServletRequest request);
void onNewSession(Session session,
HttpServletRequest request,
HttpServletResponse response);
void onInvalidateSession(HttpServletRequest request,
HttpServletResponse response);
}
38. @dgomezg
Customize SessionStorage
Different storage options could be implemented.
1) Implement a SessionRepository
public interface SessionRepository<S extends Session> {
S createSession();
void save(S session);
S getSession(String id);
void delete(String id);
}
39. @dgomezg
Customize SessionStorage
Different storage options could be implemented.
2) Inject to SessionRepositoryFilter constructor
public class SessionRepositoryFilter<S extends ExpiringSession>
extends OncePerRequestFilter {
/* … */
public SessionRepositoryFilter(SessionRepository<S> sessionRepository) {
if(sessionRepository == null) {
throw new IllegalArgumentException("SessionRepository cannot be null");
}
this.sessionRepository = sessionRepository;
}
}
42. @dgomezg
Multiple Session handling
Session id is :
1) exchanged with the client via CookieSessionStrategy
2) which is a MultipleSessionStrategy
3) so, different session IDs are kept.
43. @dgomezg
Multiple Session handling
Session id is :
1) exchanged with the client via CookieSessionStrategy
2) which is a MultipleSessionStrategy
3) so, different session IDs are kept.
4) all we have to do is specify/select the right one.
45. @dgomezg
New Session
if index is found in array, session id is used
otherwise, new session is created
<a id="addAccount" href=“http://localhost:8080/?_s=17“>
Add Account
</a>
46. @dgomezg
Compose new Session URL
We can create the new Session through the
HttpSessionManager
<a id=“addAccount" href=“${addAccountUrl}”/>
Add Account
</a>
HttpSessionManager sessionManager =
(HttpSessionManager)
httpRequest.getAttribute(HttpSessionManager.class.getName());
String addAlias = sessionManager.getNewSessionAlias(httpRequest);
httpRequest.setAttribute(“addAccountUrl",
sessionManager.encodeURL(contextPath, addAlias));
47. @dgomezg
Using session index in URLs
HttpServletResponseWrapper automatically
encodes the session id in URL.
class CookieSessionStrategy.MultiSessionHttpServletResponse
extends HttpServletResponseWrapper {
@Override
public String encodeURL(String url) {
url = super.encodeURL(url);
String alias = getCurrentSessionAlias(request);
return
CookieHttpSessionStrategy.this.encodeURL(url, alias);
}
}
49. @dgomezg
State in RESTful APIs
We could add State to REST endpoints:
• Persisted on external persistent service (redis)
• Restored into the HttpSession by the filter
• Used trasparently
51. @dgomezg
HeaderHttpSessionStrategy
Uses an specific header in Response to return session
ID
By default x-‐auth-‐token
@Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy();
}
53. @dgomezg
HeaderHttpSessionStrategy
Session header name can be customised
@Bean
public HttpSessionStrategy httpSessionStrategy() {
HeaderHttpSessionStrategy sessionStrategy =
new HeaderHttpSessionStrategy();
sessionStrategy.setHeaderName("x-custom-session-id");
return sessionStrategy;
}
55. @dgomezg
Conclusions
•HttpSession not container dependant
•Implementation
•Configuration
•Persistent store easily configurable
•Transparent for the developer
•SpringSessionFilter,
•HttpServletRequest/HttpSession Wrappers