精华内容
下载资源
问答
  • Session共享 tomcat8+redis的session共享实现,支持tomcat8
  • Session共享 tomcat7+redis的session共享实现,无需重新打包,直接放入tomcat lib中就可以使用,记得修改tomcat的content.xml
  • 主要介绍了SpringBoot Session共享实现图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 完美实现分布式集群Session共享 简单多tomcat8+redis的session共享实现,支持tomcat8、tomcat8.5、tomcat9,不能用直接联系我积分双倍返回。
  • web session 共享实现方式.pdf
  • 主要介绍了Springboot Session共享实现原理及代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 但是分布式部署的时候,我们请求的服务器可能不是同一台服务器,那么我们就必须要面对 session 共享的问题,下面介绍的是在 SpringBoot 实现 session 共享的方式 一、创建项目 创建 SpringBoot 项目,选择 Maven ...

    前言:我们知道,在单体项目中,我们将用户信息存在 session 中,那么在该 session 过期之前,我们都可以从 session 中获取到用户信息,通过登录拦截,进行操作
    但是分布式部署的时候,我们请求的服务器可能不是同一台服务器,那么我们就必须要面对 session 共享的问题,下面介绍的是在 SpringBoot 实现 session 共享的方式

    一、创建项目

    创建 SpringBoot 项目,选择 Maven 依赖
    web
    redis
    最终 pom.xml 文件如下:

    <!-- redis的依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- web的依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- session共享的依赖 -->
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    

    二、配置 Redis

    我们需要借助 redis 实现 session 共享,所以我们需要在配置文件中配置 redis 的信息

    server:
      port: 8080
    
    spring:
      redis:
        host: 127.0.0.1
        port: 6379
        database: 0
        password:
    

    我们配置了该项目的端口,以及 redis 的连接信息

    三、写接口

    package com.zyxx.session.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpSession;
    
    @RestController
    public class DemoController {
    
        /**
         * 获取项目端口
         */
        @Value("${server.port}")
        private String port;
    
        /**
         * 将信息存放在session中
         */
        @GetMapping("set")
        public String set(HttpSession session) {
            session.setAttribute("user", "hello world~~~");
            return port;
        }
    
        /**
         * 从session中获取信息
         */
        @GetMapping("get")
        public String get(HttpSession session) {
            return session.getAttribute("user") + " : " + port;
        }
    }
    
    

    我们写了一个 set,一个 get 方法,将信息存放在 session 中,从 session 中取出信息

    四、打包测试

    打包
    启动项目,分别启动在两个端口:

    java -jar .\session-0.0.1-SNAPSHOT.jar --server.port=8080
    java -jar .\session-0.0.1-SNAPSHOT.jar --server.port=8081
    

    分别启动在 8080,8081端口

    访问:http://localhost:8080/set
    在这里插入图片描述
    我们从 8080 端口,将信息保存在 session 中

    我们访问:http://localhost:8081/get
    在这里插入图片描述
    我们在 8081 端口的项目中从 session 中取出了内容:hello world~~~

    由此证明,我们的 session 共享已经成功

    五、分布式部署

    下面我们借助 nginx 代理转发访问这两个项目

    1、配置转发

    nginx 配置文件如下:
    nginx配置
    主要配置内容:

    upstream helloworld{
    	server 127.0.0.1:8080 weight=1;
    	server 127.0.0.1:8081 weight=2;
    }
    

    这里配置转发到 8080,8081 端口,并配置了权重

    location / {
    	proxy_pass http://helloworld;
        #root   html;
        #index  index.html index.htm;
    }
    

    拦截本地的所有请求,默认端口为 80

    2、启动 nginx

    nginx -s reload
    

    3、访问测试

    我们先删除 redis 里面刚刚测试保存的信息
    redis信息
    然后我们访问:

    http://localhost/set
    

    set
    这里我们可以看出,由 8080 端口的服务器完成了 set 请求,多次访问,nginx 将会根据什么配置的权重参数分配服务器来完成操作

    下面我们访问:

    http://localhost/get
    

    get
    可以看出,由 8081 端口的服务器完成了 get 请求,并成功取到了存在 session 中的数据,实现了 session 共享

    六、总结

    1、以前我们在 SSM 架构的项目中实现 session 共享,需要配置三个地方 ,一个是 web.xml 配置代理过滤器,然后在 Spring 容器中配置 Redis,最后再配置 Spring Session,相比 SpringBoot,稍有复杂
    2、我们在 SpringBoot 中实现 session 共享还是非常简单的,只需要引入依赖,简单配置即可实现
    3、实现 session 共享,帮助我们将项目分布式部署,提升服务性能有很大的意义

    如您在阅读中发现不足,欢迎留言!!!

    展开全文
  • 主要介绍了SpringBoot中使用Session共享实现分布式部署的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 此文档只是用于实现spring redis session共享的文档,并没有其他代码,都是配置,配置加入到spring配置中即可实现session共享,有不懂的可以私信我,有时间会为您解答
  • 用shiro + redis 实现session共享以及 认证的简单实例;
  • 基于redis、tomcat filter的tomcat集群session共享实现 在工程下新建sessionparameter.properties文件 具体配置参考sessionparameter_default.properties 如果配置了redis_cluster.host,则redis.pool.host的配置...
  • nginx+memcached+tomcat 负载均衡+session共享实现所有代码 教程请看:https://blog.csdn.net/dayuang2009/article/details/80312249
  • 基于Redis Cluster的Tomat的Session共享实现

    参考了下面文章的【六、tomcat session manager 配置】,使用Redis进行了session的共享配置。
    http://blog.csdn.net/zheng0518/article/details/46754343

    但是文章中提到的tomcat-redis-session-manager-master已经有2年没有更新,不支持Redis 3.0的Redis集群Redis Cluster。

    因此对源代码进行了改写。

    将原来的Jedis客户端升级为2.9.0,再有将Jedis换成JedisCluster。
    修改过的代码地址。https://github.com/zhmz1326/tomcat-redis-session-manager

    使用方法:
    1 将tomcat-redis-session-manager-2.1.0,jedis-2.9.0,commons-pool2-2.4.2这3个jar包放在tomcat/lib下。
    commons-pool2-2.4.2
    Jedis-2.9.0
    tomcat-redis-session-manager-2.1.0

    2 在tomcat/conf/context.xml增加类似如下的配置。
    redisNodes根据的redis集群的node的ip和port进行配置。
    密码(password=”redis_password”)可选。配置了redis密码的集群,才需要配置。

    <Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
    <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
         redisNodes="192.168.1.81:7001,192.168.1.81:7002,192.168.1.81:7003,192.168.1.82:7004,192.168.1.82:7005,192.168.1.82:7006"
         password="redis_password"
         timeout="300000"
         maxRedirections="6"
         maxWaitMillis="-1"
         maxTotal="1000"
         minIdle="8"
         maxIdle="100"
         maxInactiveInterval="60"/>
    展开全文
  • 本篇介绍Spring-Session的整个实现的原理。以及对核心的源码进行简单的介绍! 实现原理介绍 实现原理这里简单说明描述: 就是当Web服务器接收到http请求后,当请求进入对应的Filter进行过滤,将原本...

    做一个积极的人

    编码、改bug、提升自己

    我有一个乐园,面向编程,春暖花开!

    =================================================

    对人工智能感兴趣的伙伴,分享一个我朋友的人工智能教程。零基础!通俗易懂!风趣幽默!大家可以看看是否对自己有帮助,点击这里查看教程

    =================================================

    知其然,还要知其所以然 !

    本篇介绍Spring-Session的整个实现的原理。以及对核心的源码进行简单的介绍!

    实现原理介绍

    实现原理这里简单说明描述:

    就是当Web服务器接收到http请求后,当请求进入对应的Filter进行过滤,将原本需要由web服务器创建会话的过程转交给Spring-Session进行创建,本来创建的会话保存在Web服务器内存中,通过Spring-Session创建的会话信息可以保存第三方的服务中,如:redis,mysql等。Web服务器之间通过连接第三方服务来共享数据,实现Session共享!

    实现原理结构草图

    整个实现流程和源码详细介绍

    本次源码介绍基于上一篇内容,并且在保存Session的时候只会分析使用JedisConnectionFactory实现的RedisConnectionFactory !

    1.SessionRepositoryFilter和JedisConnectionFactory注册过程

    流程:

    SessionRepositoryFilter和JedisConnectionFactory注册过程

    说明:

    1.、启动WEB项目的时候,会读取web.xml,读取顺序content-param --> listener --> filter --> servlet
    
    2.、ContextLoaderListener监听器的作用就是启动Web容器时,自动装配ApplicationContext的配置信息
    
    3、初始化根web应用程序上下文。
    
    4、SpringHttpSessionConfiguration注册 springSessionRepositoryFilter :bean,RedisHttpSessionConfiguration 注册 sessionRedisTemplate : bean  和 sessionRepository : bean
    
    5、配置文件配置JedisConnectionFactory implements RedisConnectionFactory ,创建 jedisConnectionFactory bean
    
    

    代码分析如下:

    1. web.xml ,加载了xml配置文件,并初始化web应用上下文
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:spring/*xml</param-value>
    </context-param>
    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
      
    

    2.application-session.xml中,配置 RedisHttpSessionConfiguration的bean和JedisConnectionFactory的bean,web应用初始化加载bean!

    <!--创建一个Spring Bean的名称springSessionRepositoryFilter实现过滤器。
        筛选器负责将HttpSession实现替换为Spring会话支持。在这个实例中,Spring会话得到了Redis的支持。-->
        <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
    
        <!--创建了一个RedisConnectionFactory,它将Spring会话连接到Redis服务器。我们配置连接到默认端口(6379)上的本地主机!-->
        <!--集群Redis-->
        <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
            <!--Redis-CLuster-->
            <constructor-arg index="0" ref="redisClusterConfig"/>
    
            <!--配置Redis连接池 ,可以不配置,使用默认就行!-->
            <constructor-arg index="1" ref="jedisPoolConfig"/>
        </bean>
        
    
    1. ContextLoaderListener
    /**
    * 初始化根web应用程序上下文。
    */
    @Override
    public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
    }
    	
    

    4.RedisHttpSessionConfiguration类图

    RedisHttpSessionConfiguration类图

    RedisHttpSessionConfiguration注释
    RedisHttpSessionConfiguration继承了SpringHttpSessionConfiguration

    public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
    		implements EmbeddedValueResolverAware, ImportAware {
    		
    

    4.1 SpringHttpSessionConfiguration 创建一个名称为springSessionRepositoryFilter的bean

    @Bean
    public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(
        SessionRepository<S> sessionRepository) {
        SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(
            sessionRepository);
        sessionRepositoryFilter.setServletContext(this.servletContext);
        if (this.httpSessionStrategy instanceof MultiHttpSessionStrategy) {
            sessionRepositoryFilter.setHttpSessionStrategy(
                (MultiHttpSessionStrategy) this.httpSessionStrategy);
        }
        else {
            sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy);
        }
        return sessionRepositoryFilter;
    }
    
    
    

    4.2 创建RedisHttpSessionConfiguration#RedisTemplate bean的名称为sessionRedisTemplate

    @Bean
    public RedisTemplate<Object, Object> sessionRedisTemplate(
        RedisConnectionFactory connectionFactory) {
        //实例化 RedisTemplate 
        RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
        //设置key序列化 StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        //设置Hash key  StringRedisSerializer
        template.setHashKeySerializer(new StringRedisSerializer());
        if (this.defaultRedisSerializer != null) {
            template.setDefaultSerializer(this.defaultRedisSerializer);
        }
        //设置 connectionFactory。第五步创建的(实际connectionFactory加载过程和讲解过程顺序不一样)
        template.setConnectionFactory(connectionFactory);
        return template;
    }
    
    

    4.3 创建RedisHttpSessionConfiguration#RedisOperationsSessionRepository bean的名称为sessionRepository

    @Bean
    public RedisOperationsSessionRepository sessionRepository(
        //使用sessionRedisTemplate bean
        @Qualifier("sessionRedisTemplate") RedisOperations<Object, Object> sessionRedisTemplate,
        ApplicationEventPublisher applicationEventPublisher) {
    
        //實例化RedisOperationsSessionRepository
        RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
            sessionRedisTemplate);
        //設置applicationEventPublisher
        sessionRepository.setApplicationEventPublisher(applicationEventPublisher);
        //設置最大的失效時間 maxInactiveIntervalInSeconds = 1800
        sessionRepository
            .setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
        if (this.defaultRedisSerializer != null) {
            sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
        }
    
        String redisNamespace = getRedisNamespace();
        if (StringUtils.hasText(redisNamespace)) {
            sessionRepository.setRedisKeyNamespace(redisNamespace);
        }
    
        sessionRepository.setRedisFlushMode(this.redisFlushMode);
        return sessionRepository;
    }
    
    
    1. 创建 RedisConnectionFactory bean为 jedisConnectionFactory

    JedisConnectionFactory类图

    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    
    

    2.SessionRepositoryFilter添加到FilterChain

    流程:

    SessionRepositoryFilter添加到FIlterChain

    说明:

    1 和 2、在Servlet3.0规范中,Servlet容器启动时会自动扫描javax.servlet.ServletContainerInitializer的实现类,在实现类中我们可以定制需要加载的类。 通过注解@HandlesTypes(WebApplicationInitializer.class),让Servlet容器在启动该类时,会自动寻找所有的WebApplicationInitializer实现类。
    
    2.1、insertSessionRepositoryFilter 方法通过filterName获取 SessionRepositoryFilter ,并创建了 new DelegatingFilterProxy(filterName);
    
    3 和 4、然后将filter添加到FilterChain中
    
    
    

    1.ServletContainerInitializer的实现类加载和通过注解@HandlesTypes(WebApplicationInitializer.class)实现类的加载

    //加载实现类
    @HandlesTypes(WebApplicationInitializer.class)
    //SpringServletContainerInitializer实现ServletContainerInitializer
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
    
    //------------
    
    

    2.AbstractHttpSessionApplicationInitializer实现WebApplicationInitializer进行加载

    @Order(100)
    public abstract class AbstractHttpSessionApplicationInitializer
        implements WebApplicationInitializer {
    
    

    2.1 onStartup

    public void onStartup(ServletContext servletContext) throws ServletException {
        beforeSessionRepositoryFilter(servletContext);
        if (this.configurationClasses != null) {
            AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
            rootAppContext.register(this.configurationClasses);
            servletContext.addListener(new ContextLoaderListener(rootAppContext));
        }
        //添加Filter
        insertSessionRepositoryFilter(servletContext);
        afterSessionRepositoryFilter(servletContext);
    }
    
    

    2.1.1.insertSessionRepositoryFilter

    /**
    	 * 注册springSessionRepositoryFilter
    	 * @param servletContext the {@link ServletContext}
    	 */
    private void insertSessionRepositoryFilter(ServletContext servletContext) {
        // DEFAULT_FILTER_NAME = "springSessionRepositoryFilter"
        String filterName = DEFAULT_FILTER_NAME;
        //通过filterName创建 DelegatingFilterProxy
        DelegatingFilterProxy springSessionRepositoryFilter = new DelegatingFilterProxy(
            filterName);
        String contextAttribute = getWebApplicationContextAttribute();
        if (contextAttribute != null) {
            springSessionRepositoryFilter.setContextAttribute(contextAttribute);
        }
        //根据filterName和上下文添加Filter到FilterChain
        registerFilter(servletContext, true, filterName, springSessionRepositoryFilter);
    }
    
    
    1. registerFilter
    private void registerFilter(ServletContext servletContext,
                                boolean insertBeforeOtherFilters, String filterName, Filter filter) {
        Dynamic registration = servletContext.addFilter(filterName, filter);
        if (registration == null) {
            throw new IllegalStateException(
                "Duplicate Filter registration for '" + filterName
                + "'. Check to ensure the Filter is only configured once.");
        }
        //是否支持异步,默认 true
        registration.setAsyncSupported(isAsyncSessionSupported());
        //得到DispatcherType springSessionRepositoryFilter
        EnumSet<DispatcherType> dispatcherTypes = getSessionDispatcherTypes();
        //添加一个带有给定url模式的筛选器映射和由这个FilterRegistration表示的过滤器的分派器类型。 过滤器映射按照添加它们的顺序进行匹配。
        registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters,
                                              "/*");
    }
    
    1. addFilter将Filter添加到ServletContext中
    public FilterRegistration.Dynamic addFilter(
        String filterName, Filter filter);
    
    

    3.SessionRepositoryFilter拦截过程

    流程:

    SessionRepositoryFilter拦截过程

    说明:

    1、请求被DelegatingFilterProxy : 拦截到,然后执行doFilter方法,在doFilter中找到执行的代理类。
    2、OncePerRequestFilter : 代理Filter执行doFilter方法,然后调用抽象方法doFilterInternal
    3、SessionRepositoryFilter 继承了OncePerRequestFilter,实现了doFilterInternal,这个方法一个封装一个wrappedRequest,通过执行commitSession保存session信息到redis
    
    

    1请求进来,被DelegatingFilterProxy 拦截到,在web.xml中进行了配置
    1.1 执行doFilter

    如果没有指定目标bean名称,请使用筛选器名称。
    @Override
    	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    
        // 如果需要,延迟初始化委托。 necessary.
        Filter delegateToUse = this.delegate;
        if (delegateToUse == null) {
            synchronized (this.delegateMonitor) {
                if (this.delegate == null) {
                    WebApplicationContext wac = findWebApplicationContext();
                    if (wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: " +
                                                        "no ContextLoaderListener or DispatcherServlet registered?");
                    }
                    this.delegate = initDelegate(wac);
                }
                delegateToUse = this.delegate;
            }
        }
    
        // 让委托执行实际的doFilter操作
        invokeDelegate(delegateToUse, request, response, filterChain);
    }
    

    1.2 initDelegate

    protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        //可以获取到SessionRepositoryFilter [备注1]
        Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
    }
    
    //[备注1] 因为 :SessionRepositoryFilter是一个优先级最高的javax.servlet.Filter
    /*
    @Order(SessionRepositoryFilter.DEFAULT_ORDER)
    public class SessionRepositoryFilter<S extends ExpiringSession>
    		extends OncePerRequestFilter {
    
    */
    
    1. delegate.doFilter();
    protected void invokeDelegate(
        Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        //代理去执行doFilter,代理为SessionRepositoryFilter
        delegate.doFilter(request, response, filterChain);
    }
    
    

    2.1 OncePerRequestFilter#doFilter

    public final void doFilter(ServletRequest request, ServletResponse response,
                               FilterChain filterChain) throws ServletException, IOException {
    
        if (!(request instanceof HttpServletRequest)
            || !(response instanceof HttpServletResponse)) {
            throw new ServletException(
                "OncePerRequestFilter just supports HTTP requests");
        }
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        boolean hasAlreadyFilteredAttribute = request
            .getAttribute(this.alreadyFilteredAttributeName) != null;
    
        if (hasAlreadyFilteredAttribute) {
    
            //在不调用此过滤器的情况下进行…
            filterChain.doFilter(request, response);
        }
        else {
            // 调用这个过滤器…
            request.setAttribute(this.alreadyFilteredAttributeName, Boolean.TRUE);
            try {
                //doFilterInternal是个抽象方法
                doFilterInternal(httpRequest, httpResponse, filterChain);
            }
            finally {
                // 删除此请求的“已过滤”请求属性。
                request.removeAttribute(this.alreadyFilteredAttributeName);
            }
        }
    }
    
    
    1. 执行SessionRepositoryFilter#doFilterInternal
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
        //使用HttpServletRequest 、HttpServletResponse和servletContext创建一个SessionRepositoryRequestWrapper
    
        SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
            request, response, this.servletContext);
        SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
            wrappedRequest, response);
    
        //使用CookieHttpSessionStrategy重新包装了 HttpServletRequest
        HttpServletRequest strategyRequest = this.httpSessionStrategy
            .wrapRequest(wrappedRequest, wrappedResponse);
        HttpServletResponse strategyResponse = this.httpSessionStrategy
            .wrapResponse(wrappedRequest, wrappedResponse);
    
        try {
            //执行其他过滤器
            filterChain.doFilter(strategyRequest, strategyResponse);
        }
        finally {
            //保存session信息
            wrappedRequest.commitSession();
        }
    }
    
    

    4 .wrappedRequest.commitSession() 看下第四大点分析

    4.SessionRepository保存session数据

    流程:
    SessionRepository保存session数据

    说明:

    1、提交session保存
    2、获取当前session,这一步比较重要,获取了一个HttpSessionWrapper,这个HttpSessionWrapper替换了HTTPSession
    3、wrappedSession获取当前的Session
    4、使用 RedisTemplate 保存Session内容,并通过调用RedisConnection 使用它的实现类JedisClusterConnection获取redis连接
    
    

    1.commitSession

    /**
    *使用HttpSessionStrategy将会话id写入响应。 *保存会话。
    */
    private void commitSession() {
        HttpSessionWrapper wrappedSession = getCurrentSession();
        if (wrappedSession == null) {
            if (isInvalidateClientSession()) {
                SessionRepositoryFilter.this.httpSessionStrategy
                    .onInvalidateSession(this, this.response);
            }
        }
        else {
            S session = wrappedSession.getSession();
            SessionRepositoryFilter.this.sessionRepository.save(session);
            if (!isRequestedSessionIdValid()
                || !session.getId().equals(getRequestedSessionId())) {
                SessionRepositoryFilter.this.httpSessionStrategy.onNewSession(session,
                                                                              this, this.response);
            }
        }
    }
    

    2.getCurrentSession

    会话存储库请求属性名。
    public static final String SESSION_REPOSITORY_ATTR = SessionRepository.class
        .getName();
    
    private static final String CURRENT_SESSION_ATTR = SESSION_REPOSITORY_ATTR
    			+ ".CURRENT_SESSION";
    
    private HttpSessionWrapper getCurrentSession() {
        return (HttpSessionWrapper)
            //获取session
            getAttribute(CURRENT_SESSION_ATTR);
    }
    
    /**
         * 此方法的默认行为是在包装请求对象上调用getAttribute(字符串名称)。
         */
    public Object getAttribute(String name) {
        //这里的request就是上面封装的
        return this.request.getAttribute(name);
    }
    

    3 .wrappedSession.getSession

    //返回 RedisSession
    S session = wrappedSession.getSession();
    //-------------------------
    public S getSession() {
        return this.session;
    }
    class ExpiringSessionHttpSession<S extends ExpiringSession> implements HttpSession {
        private S session;
    
    
     final class RedisSession implements ExpiringSession {
    
    

    4.save,实际是调用 RedisOperationsSessionRepository的 RedisOperations 操作

    SessionRepositoryFilter.this.sessionRepository.save(session);
    
    //this.sessionRepository =  SessionRepository<S> sessionRepository;
    
    //--------------------------------
    //这个RedisOperationsSessionRepository是之前就创建好的
    public class RedisOperationsSessionRepository implements
        FindByIndexNameSessionRepository<RedisOperationsSessionRepository.RedisSession>,
    MessageListener {
    
    
        public interface FindByIndexNameSessionRepository<S extends Session>
            extends SessionRepository<S> {
    
            //---------------------------
    
    
            public void save(RedisSession session) {
                //4.1saveDelta
                session.saveDelta();
                if (session.isNew()) {
                    //4.2调用
                    String sessionCreatedKey = getSessionCreatedChannel(session.getId());
                    //4.3convertAndSend
                    //RedisOperations = this.sessionRedisOperations
                    this.sessionRedisOperations.convertAndSend(sessionCreatedKey, session.delta);
                    session.setNew(false);
                }
            }
    	
    	
    

    其中RedisOperationsSessionRepository 里面介绍保存的详细过程,具体请看文档说明:

    Class RedisOperationsSessionRepository

    因为 RedisTemplate implements RedisOperations,实际进行操作的是RedisTemplate,RedisTemplate通过RedisConnection进行数据add和remove等

    public class RedisTemplate<K, V>
    extends RedisAccessor
    implements RedisOperations<K, V>, BeanClassLoaderAware
    

    总结

    本系列到这里也就结束了,本次话的整个流程图,会上传到github上,使用Jude打开就可以看!

    如果有什么地方写的不对或者有想和我一起探讨一下的,欢迎加我的QQ或者QQ群!

    记录一个小点:

    Spring Session + Redis实现分布式Session共享 有个非常大的缺陷, 无法实现跨域名共享session , 只能在单台服务器上共享session , 因为是依赖cookie做的 , cookie 无法跨域 pring Session一般是用于多台服务器负载均衡时共享Session的,都是同一个域名,不会跨域。你想要的跨域的登录,可能需要SSO单点登录。

    参考博文

    【Spring】Spring Session的简单搭建与源码阅读

    利用spring session解决共享Session问题

    Spring Session解决分布式Session问题的实现原理

    spring-session简介、使用及实现原理


    本系列教程

    【入门】分布式Session一致性入门简介

    【第一篇】Spring-Session实现Session共享入门教程

    【第二篇】Spring-Session实现Session共享Redis集群方式配置教程

    【第三篇】Spring-Session实现Session共享实现原理以及源码解析

    本系列的源码下载地址:learn-spring-session-core


    谢谢你的阅读,如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到!祝你每天开心愉快!



    不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

    愿你我在人生的路上能都变成最好的自己,能够成为一个独挡一面的人

    © 每天都在变得更好的阿飞云

    展开全文
  • 基于redis的session共享实现

    千次阅读 2017-10-10 17:31:10
    总得来说,就是自己写了一个拦截器,在拦截器里面把session放进了redis里面,从而实现session共享。 首先贴出web.xml的配置,和普通拦截器的配置一样: sessionFilter com.dnkx.web.filter.SessionFil

    项目上线,闲来无事,把项目里用到的session共享贴出来,因为有多个项目要用到这个功能,所以这个部分功能独立出来了。总得来说,就是自己写了一个拦截器,在拦截器里面把session放进了redis里面,从而实现了session共享。

    首先贴出web.xml的配置,和普通拦截器的配置一样:

    <filter>
        <filter-name>sessionFilter</filter-name>
        <filter-class>com.dnkx.web.filter.SessionFilter</filter-class>
      </filter>
      <filter-mapping>
        <filter-name>sessionFilter</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>
      <!-- session过期时间 -->
      <session-config>
        <session-timeout>30</session-timeout>
      </session-config>

    然后是sessionFilter:

    public class SessionFilter extends HttpServlet implements Filter {
    
        /**
    	 * 
    	 */
    	private static final long serialVersionUID = -5723213015901607291L;
    
    	@Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            HttpServletResponse response = (HttpServletResponse) servletResponse;
    
            request.setAttribute("hostname", InetAddress.getLocalHost().getHostName());
    
            filterChain.doFilter(new HttpServletRequestWrapper(request, response),
                    servletResponse);
        }
    }

    上面两段代码都是基础,没什么好解释的,下面贴出关键的HttpServletRequestWrapper类:


    public class HttpServletRequestWrapper extends
            javax.servlet.http.HttpServletRequestWrapper {
    
        private static final String cookieId = "csid";
        static String sessionHoldKey = "session_hold_key";
    
        private String sessionId;
    
        public HttpServletRequestWrapper(HttpServletRequest request, HttpServletResponse response) {
            super(request);
            sessionIdManage(request, response);
        }
    
        @SuppressWarnings({ "rawtypes", "unchecked" })
    	private void sessionIdManage(HttpServletRequest request, HttpServletResponse response) {
            Cookie[] cookies = request.getCookies();
            if (cookies == null ) {
               // logger.warn("request.getCookies() returns null,I didn't see it before");
            } else {
                for (int i = 0; i < cookies.length; i++) {
                    Cookie sCookie = cookies[i];
                    if (sCookie.getName().equals(cookieId)) {
                        sessionId = sCookie.getValue();
                    }
                }
            }
            if (sessionId == null || sessionId.length() == 0) {
                sessionIdCreate(response);
            }
            HashOperations hashOps = WebComponentFactory.getSessionRedisTemplate().opsForHash();
            Long lastAccessedTime = (Long) hashOps.get(sessionId, sessionHoldKey);
            long maxInactiveInterval = request.getSession().getMaxInactiveInterval() * 1000;
            if (lastAccessedTime == null || maxInactiveInterval > 0 && System.currentTimeMillis() - lastAccessedTime > maxInactiveInterval) {
                sessionIdCreate(response);
            }
            hashOps.put(sessionId, sessionHoldKey, System.currentTimeMillis());
            WebComponentFactory.getSessionRedisTemplate().expire(sessionId,3, TimeUnit.DAYS);
        }
    
        private void sessionIdCreate(HttpServletResponse response) {
            sessionId = java.util.UUID.randomUUID().toString();
            Cookie mycookies = new Cookie(cookieId, sessionId);
            mycookies.setPath("/");
            response.addCookie(mycookies);
        }
    
        public HttpSession getSession(boolean create) {
            return new HttpSessionSidWrapper(this.sessionId, super.getSession(create));
        }
    
        public HttpSession getSession() {
            return new HttpSessionSidWrapper(this.sessionId, super.getSession());
        }
    
    }

    上面这段代码是session共享的关键部分,这里需要了解cookie和session的机制,这里是用一个自己的cookie存了自己生成的sessionId返回给客户端,每次请求在根据cookie的值来做session的操作,sessionId是存在redis里面的,根据session做redis的get,set等操作,代码也很容易看懂。

    完整源代码下载地址

    展开全文
  • J2EE同域中跨项目session共享实现 1、 背景 一个tomcat下跑两个web项目:appA,appB 2、 目的 appA项目中的session设置值能被appB项目获取,反之亦然。 3、 实现思路(以下假设项目appA的session被项目appB共享
  • 由于项目需要测试日均千万交易的性能测试,故使用Nginx作为反向代理实现了多个Tomcat的负载均衡,为了实现多个Tomcat之间的session共享,使用了开源的Memcached-Session-Manager框架。以下是项目构建过程: 1)构建...
  • 1.背景 随着互联网的日益壮大,网站的pv和uv成线性或者指数倍的增加....但是在服务集群中,session共享往往是一个比较头疼的问题。因为session是在服务器端保存的,如果用户跳转到其他服务器的话,session就会丢
  • 转载网上看到的两篇关于集群中实现session共享的两篇文章,个人觉得不错,学习了: 对Web服务器进行集群,Session的安全和同步是最大的问题,实现Session同步有很多种方案,常见的可能的方式有: 1、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 185,627
精华内容 74,250
关键字:

session共享怎么实现