Spring Session 实现分布式会话管理

Stella981
• 阅读 360

1、分布式会话管理是什么?

在Web项目开发中,会话管理是一个很重要的部分,用于存储与用户相关的数据。通常是由符合session规范的容器来负责存储管理,也就是一旦容器关闭,重启会导致会话失效。因此打造一个高可用性的系统,必须将session管理从容器中独立出来。

2、分布式会话管理的解决方案选用

实现方案有很多种,下面简单介绍下:

  第一种是使用容器扩展来实现,大家比较容易接受的是通过容器插件来实现,比如基于Tomcat的tomcat-redis-session-manager,基于Jetty的jetty-session-redis等等。好处是对项目来说是透明的,无需改动代码。不过前者目前还不支持Tomcat 8,或者说不太完善。个人觉得由于过于依赖容器,一旦容器升级或者更换意味着又得从新来过。并且代码不在项目中,对开发者来说维护也是个问题。

  第二种是自己写一套会话管理的工具类,包括Session管理和Cookie管理,在需要使用会话的时候都从自己的工具类中获取,而工具类后端存储可以放到Redis中。很显然这个方案灵活性最大,但开发需要一些额外的时间。并且系统中存在两套Session方案,很容易弄错而导致取不到数据。

  第三种是使用框架的会话管理工具,也就是本文要说的spring-session,可以理解是替换了Servlet那一套会话管理,既不依赖容器,又不需要改动代码,并且是用了spring-data-redis那一套连接池,可以说是最完美的解决方案。当然,前提是项目要使用Spring Framework才行。

3、为什么使用Spring Session

Spring Session为企业级Java应用的session管理带来了革新,使得以下的功能更加容易实现:

  • 将session所保存的状态卸载到特定的外部session存储中,如Redis或Apache Geode中,它们能够以独立于应用服务器的方式提供高质量的集群。
  • 当用户使用WebSocket发送请求的时候,能够保持HttpSession处于活跃状态。
  • 在非Web请求的处理代码中,能够访问session数据,比如在JMS消息的处理代码中。
  • 支持每个浏览器上使用多个session,从而能够很容易地构建更加丰富的终端用户体验。
  • 控制session id如何在客户端和服务器之间进行交换,这样的话就能很容易地编写Restful API,因为它可以从HTTP 头信息中获取session id,而不必再依赖于cookie。

参考:http://m.blog.csdn.net/article/details?id=51483471

4、Spring Session分布式会话解决方案

web.xml配置:

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

编写配置,注解@EnableRedisHttpSession通过Import,引入了RedisHttpSessionConfiguration配置类。该配置类通过@Bean注解,向Spring容器中注册了一个SessionRepositoryFilter(SessionRepositoryFilter的依赖关系:SessionRepositoryFilter --> SessionRepository --> RedisTemplate --> RedisConnectionFactory)。

    import org.springframework.context.annotation.Bean;  
    import org.springframework.data.redis.connection.RedisConnectionFactory;  
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;  
    import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;  
      
    @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)  
    public class RedisHttpSessionConfig {  
      
        @Bean  
        public RedisConnectionFactory connectionFactory() {  
            JedisConnectionFactory connectionFactory = new JedisConnectionFactory();  
            connectionFactory.setPort(6379);  
            connectionFactory.setHostName("127.0.0.1");  
            return connectionFactory;  
        }  
    } 

更复杂的Java Confg,需要加入spring 容器。

package io.flysium.framework.session;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.session.data.redis.RedisFlushMode;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
/**
 * Spring Session分布式会话解决方案
 * 
 * @author SvenAugustus(蔡政滦)  e-mail: SvenAugustus@outlook.com
 * @version 1.0
 */
@Configuration
@EnableScheduling
public class RedisHttpSessionConfig extends RedisHttpSessionConfiguration {
    /**
     * Spring Data Redis 的连接工厂配置,必选
     */
    @Bean(name = "connectionFactory")
    public RedisConnectionFactory connectionFactory() {
        JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
        connectionFactory.setPort(6379);
        connectionFactory.setHostName("127.0.0.1");
        return connectionFactory;
    }
    /**
     * Spring Data Redis 的会话存储仓库配置,可选
     */
    @Bean(name = "sessionRepository")
    public RedisOperationsSessionRepository sessionRepository(
            RedisOperations<Object, Object> sessionRedisTemplate,
            ApplicationEventPublisher applicationEventPublisher) {
        this.setMaxInactiveIntervalInSeconds(Integer.valueOf(900)); // 单位:秒
        this.setRedisNamespace(getApplicationName());
        this.setRedisFlushMode(RedisFlushMode.ON_SAVE);
        return super.sessionRepository(sessionRedisTemplate, applicationEventPublisher);
    }
    /**
     * Spring Data Redis 的默认序列化工具,可选
     */
    @Bean(name = "springSessionDefaultRedisSerializer")
    public RedisSerializer springSessionDefaultRedisSerializer() {
        RedisSerializer defaultSerializer = new JdkSerializationRedisSerializer();
        //RedisSerializer defaultSerializer = new FastJsonStringRedisSeriaziler(
        //        Charset.forName("utf-8"));
        return defaultSerializer;
    }
    /**
     * Session会话策略配置,可选
     * 
     * 1、Spring Session 默认支持Cookie存储当前session的id,
     *         即CookieHttpSessionStrategy。
     * 2、Spring Session 支持RESTFul APIS,响应头回返回x-auth-token,来标识当前session的token,
     *     即HeaderHttpSessionStrategy。
     */
    /*@Bean(name = "httpSessionStrategy")
    public HttpSessionStrategy httpSessionStrategy() {
        HeaderHttpSessionStrategy headerHttpSessionStrategy = new HeaderHttpSessionStrategy();
        headerHttpSessionStrategy.setHeaderName("Auth-Token");
        return headerHttpSessionStrategy;
    }*/
    /**
     * Session会话策略为 CookieHttpSessionStrategy 情况下配置的 Cookie序列化工具,可选
     */
    @Bean(name = "cookieSerializer")
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
        // Cookie名称
        cookieSerializer.setCookieName(
                new StringBuilder(getApplicationName()).append("SESSION").toString());
        // HttpOnly
        cookieSerializer.setUseHttpOnlyCookie(true);
        // HTTPS定义
        //cookieSerializer.setUseSecureCookie(true);
        // 解决子域问题:把cookiePath的返回值设置为统一的根路径就能让session id从根域获取,
        //这样同根下的所有web应用就可以轻松实现单点登录共享session
        cookieSerializer.setCookiePath("/");
        return cookieSerializer;
    }
    private String getApplicationName() {
        return "app";
    }
}

5、如何查看session数据?

(1)Http Session数据(spring:session:命名空间:sessions:xxxx)在Redis中是以Hash结构存储的。

Spring Session 实现分布式会话管理

Spring Session 实现分布式会话管理

(2)Http Session过期数据(spring:session:命名空间:expirations:xxxx)以Set结构保存的。

记录了所有session数据应该被删除的时间(即最新的一个session数据过期的时间)。

Spring Session 实现分布式会话管理

6、Spring Session原理讲解

所有的request都会经过SessionRepositoryFilter,而 SessionRepositoryFilter是一个优先级最高的javax.servlet.Filter,它使用了一个SessionRepositoryRequestWrapper类接管了Http Session的创建和管理工作。

   public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {  
      
            public SessionRepositoryRequestWrapper(HttpServletRequest original) {  
                    super(original);  
            }  
      
            public HttpSession getSession() {  
                    return getSession(true);  
            }  
      
            public HttpSession getSession(boolean createNew) {  
                    // create an HttpSession implementation from Spring Session  
            }  
      
            // ... other methods delegate to the original HttpServletRequest ...  
    }
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
2年前
springboot+session(redis)(另附上:ip地址无法访问Redis)
springboot的版本:<version2.1.6.RELEASE</version搭建springboot框架涉及到session管理,交给springboot框架管理,同时为了以后分布式或集群等的扩展,故将session存储到Redis数据库中。理解::session交给spring管理,spring将session信息存储到
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Spring Shiro 使用默认的Session会话管理
项目中用到了shirosession会话管理机制,今天来总结一下,以下都是在springboot框架实现。shiro的session管理机制很完善,也是独立于j2ee容器且不依赖的,所以我们完全可以使用shiro提供给我们的session会话管理来实现我们的业务逻辑,默认提供的sessionDAO是memorySessionDAO,这里也主要讲它的配
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Easter79 Easter79
2年前
Spring整合Hibernate 之上下文 Session 持久化对象和HibernateTemp
HibernateTemplate和session1.Spring的HibernateTemplate可以管理会话和事务,简化DAO实现.但使用HibernateTemplate意味着DAO必须依赖于Spring的
Stella981 Stella981
2年前
Django组件——cookie与session
Django组件——cookie与session<fontcolor00bff一、会话跟踪技术</font<fontcolorff7f501、什么是会话跟踪技术</font先了解一下什么是会话。可以把
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这