Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Managing user's data with Spring Session

10,794 views

Published on

Slides from my "Managging user's data with Spring Session" talk at Spring IO 2015 in Barcelona

Published in: Technology

Managing user's data with Spring Session

  1. 1. Managing user’s data with Spring Session David  Gómez  G.   @dgomezg
  2. 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
  3. 3. @dgomezg Session  Data:  State  of  the  art. • Sometimes  you  need/want  to  keep  data  from   the  user
  4. 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
  5. 5. @dgomezg Scalability:  Clustering,  PASS  and     Cloud
  6. 6. @dgomezg Session  Data:  Caveats • Data  should  be  Serializable   • Servlet  Container  specific   • Implementation   • Persistent  storage  configuration.
  7. 7. @dgomezg Spring  session  motivation • Provide  a  mechanism  to  store  &  configure   session  data  handling
  8. 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
  9. 9. @dgomezg INTRODUCING   SPRING  SESSION 9
  10. 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
  11. 11. @dgomezg Architecture SessionRepositoryFilter Other  filters DispatcherServlet HttpRequestWrapper HttpSessionWrapper Transparently  replaces     HttpRequest  &  HttpSession
  12. 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. 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. 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. 15. @dgomezg SessionRepositoryFilter Bootstraps  Spring  Session  architecture   Servlet  3.0:    Use  AbstractHttpSessionApplicationInitializer   public class Initializer
 extends AbstractHttpSessionApplicationInitializer {
 
 public Initializer() {
 super(Config.class);
 }
 }
  16. 16. @dgomezg The  Big  Picture
  17. 17. @dgomezg USING  SPRING  SESSION  IN  YOUR   APPLICATION 17
  18. 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
  19. 19. @dgomezg Step  1:  Setup  dependencies <dependencies> <dependency> <groupId>org.springframework.session</groupId>
 <artifactId>spring-session</artifactId>
 <version>1.0.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.session</groupId>
 <artifactId>spring-session-data-redis</artifactId>
 <version>1.0.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId>
 <artifactId>spring-web</artifactId>
 <version>4.1.6.RELEASE</version> </dependency> </dependencies>
  20. 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. 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. 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. 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. 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. 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. 26. @dgomezg Step  5:  Bootstrap  SpringSession public class Initializer
 extends AbstractHttpSessionApplicationInitializer {
 
 public Initializer() {
 super(Config.class);
 }
 } Define  a  DelegatingFilterProxy  with  specific  name   springSessionRepositoryFilter
  27. 27. @dgomezg Step  6:  Deploy  and  test $ curl https://localhost:8080/user/session?attr=username&val=dgomezg $ $ ./redis-cli -p 6379 127.0.0.1:6379> keys * 1) "spring:session:expirations:1430159640000" 2) "spring:session:sessions:c7f8d8e3-df49-43d1-866a-ca442c99df91" 127.0.0.1:6379>
  28. 28. @dgomezg Step  6:  Deploy  and  test $ curl https://localhost:8080/user/session?attr=username&val=dgomezg $ $ ./redis-cli -p 6379 127.0.0.1:6379> keys * 1) "spring:session:expirations:1430159640000" 2) "spring:session:sessions:c7f8d8e3-df49-43d1-866a-ca442c99df91" 127.0.0.1:6379> 127.0.0.1:6379> hgetall spring:session:sessions:c7f8d8e3-df49-43d1-866a-ca442c99df91 1) "lastAccessedTime" 2) "xacxedx00x05srx00x0ejava.lang.Long;x8bxe4x90xccx8f#xdfx02x00x01Jx00x05valuexr x00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x01Lxfcx0cx84?" 3) "maxInactiveInterval" 4) "xacxedx00x05srx00x11java.lang.Integerx12xe2xa0xa4xf7x81x878x02x00x01I x00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00ab" 5) "creationTime" 6) "xacxedx00x05srx00x0ejava.lang.Long;x8bxe4x90xccx8f#xdfx02x00x01Jx00x05valuexr x00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x01Lxfcx0ckx01" 7) "sessionAttr:username" 8) "xacxedx00x05tx00adgomezg" 127.0.0.1:6379>
  29. 29. @dgomezg FURTHER  CUSTOMIZATION 29
  30. 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. 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. 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
  33. 33. @dgomezg CookieSessionStrategy Default  strategy  on  RedisHttpSessionConfiguration   Sets  a  Cookie  with  the  session  ID.
  34. 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. 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. 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);
 }

  37. 37. @dgomezg USE  CASE  1   CHANGE  PERSISTENCE  STORE 37
  38. 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. 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;
 }
 }
  40. 40. @dgomezg USE  CASE  2   MULTIPLE  USER  SESSIONS 40
  41. 41. @dgomezg Multiple  Session  Handling Session  id  is  :   1)  exchanged  with  the  client  via  CookieSessionStrategy   2)  which  is  a  MultipleSessionStrategy
  42. 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. 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.
  44. 44. @dgomezg Session  selection Specific  Session  selected  by  parameter  ?_s=#alias   http://localhost:8080/?_s=0  
  45. 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. 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. 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);
 }
 }

  48. 48. @dgomezg USE  CASE  3   RESTORING  STATE  TO  RESTFUL  APIS 48
  49. 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  
  50. 50. @dgomezg HeaderHttpSessionStrategy Uses  an  specific  header  in  Response  to  return   session  ID @Bean
 public HttpSessionStrategy httpSessionStrategy() {
 return new HeaderHttpSessionStrategy();
 }
  51. 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();
 }
  52. 52. @dgomezg HeaderHttpSessionStrategy Use  an  HeaderHttpSessionStrategy @Bean
 public HttpSessionStrategy httpSessionStrategy() {
 return new HeaderHttpSessionStrategy();
 }
  53. 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;
 }

  54. 54. @dgomezg CONCLUSIONS 54
  55. 55. @dgomezg Conclusions •HttpSession  not  container  dependant   •Implementation   •Configuration   •Persistent  store  easily  configurable   •Transparent  for  the  developer     •SpringSessionFilter,     •HttpServletRequest/HttpSession  Wrappers
  56. 56. @dgomezg THANKS! 56 Reach  me  on dgomezg@autentia.com @dgomezg Resources Spring  guides Spring  samples  repository

×