精华内容
下载资源
问答
  • 多服务器共享session的方法
    2020-03-31 10:10:26

    伴随网站业务规模和访问量的逐步发展,原本由单台服务器、单个域名的迷你网站架构已经无法满足发展需要。

    此时我们可能会购买更多服务器,并且启用多个二级子域名以频道化的方式,根据业务功能将网站分布部署在独立的服务器上;或通过负载均衡技术(如:DNS轮询、Radware、F5、LVS等)让多个频道共享一组服务器。

     

    OK,头脑中我们已经构思了这样的解决方案,不过进入深入开发后新的技术问题又随之而来:

    我们把网站程序分布部署到多台服务器上,而且独立为几个二级域名,由于Session受实现原理的局限(PHP中Session默认以文件的形式保存在本地服务器的硬盘),使得我们的网站用户不得不经常在几个频道间来回输入用户名、密码登入,导致用户体验大打折扣;另外,原本程序可以直接从用户Session变量中读取的资料(如:昵称、积分、登入时间等),因为无法跨服务器同步更新Session 变量,迫使开发人员必须实时读写数据库,从而增加了数据库的负担。

    于是,解决网站跨服务器之间的Session共享方案需求变得迫切起来,最终催生了多种解决方案,下面列举4种较为可行的方案进行对比探讨:

    1. 基于NFS的Session共享

    NFS是Net FileSystem的简称,最早由Sun公司为解决Unix网络主机间的目录共享而研发。

    这个方案实现最为简单,无需做过多的二次开发,仅需将共享目录服务器mount到各频道服务器的本地session目录即可,缺点是NFS依托于复杂的安全机制和文件系统,因此并发效率不高,尤其对于session这类高并发读写的小文件,会由于共享目录服务器的io-wait过高,最终拖累前端WEB应用程序的执行效率。

     

    2. 基于数据库的Session共享

    首选当然是大名鼎鼎的Mysql数据库,并且建议使用内存表Heap,提高session操作的读写效率。这个方案的实用性比较强,相信大家普遍在使用,它的缺点在于session的并发读写能力取决于Mysql数据库的性能,同时需要自己实现session淘汰逻辑,以便定时从数据表中更新、删除 session记录,当并发过高时容易出现表锁,虽然我们可以选择行级锁的表引擎,但不得不否认使用数据库存储Session还是有些杀鸡用牛刀的架势。

     

    3. 基于Cookie的Session共享

    这个方案我们可能比较陌生,但它在大型网站中还是比较普遍被使用。原理是将全站用户的Session信息加密、序列化后以Cookie的方式,统一种植在根域名下(如:.host.com),利用浏览器访问该根域名下的所有二级域名站点时,会传递与之域名对应的所有Cookie内容的特性,从而实现用户的Cookie化Session 在多服务间的共享访问。

    这个方案的优点无需额外的服务器资源;缺点是由于受http协议头信心长度的限制,仅能够存储小部分的用户信息,同时Cookie化的 Session内容需要进行安全加解密(如:采用DES、RSA等进行明文加解密;再由MD5、SHA-1等算法进行防伪认证),另外它也会占用一定的带宽资源,因为浏览器会在请求当前域名下任何资源时将本地Cookie附加在http头中传递到服务器。

     

    4. 基于Memcache的Session共享

    Memcache由于是一款基于Libevent多路异步I/O技术的内存共享系统,简单的Key + Value数据存储模式使得代码逻辑小巧高效,因此在并发处理能力上占据了绝对优势,目前本人所经历的项目达到2000/秒 平均查询,并且服务器CPU消耗依然不到10%。

    另外值得一提的是Memcache的内存hash表所特有的Expires数据过期淘汰机制,正好和Session的过期机制不谋而合,降低了过期Session数据删除的代码复杂度,对比“基于数据库的存储方案”,仅这块逻辑就给数据表产生巨大的查询压力。

    总结:

    关于SESSION共享
    不过由于nfs的锁机制,在高并发情况下会有效率问题,比如想用作共享session的目录的话,可能nfs会变成性能的瓶颈,这里,有这么篇文章,写得很具体,基本就是分文件夹存储的形式:
    主要即:
    1. 基于NFS的Session共享

    2. 基于数据库的Session共享

    3. 基于Cookie的Session共享

    4. 基于Memcache的Session共享

    nfs的瓶颈也许就在nfs锁机制上,数据库想必是网络传输和磁盘io,cookie也许会存在一些安全问题,否则就是会需要加密解密的机制,耗费cpu和网络传输,四种最快的想必是memcache的共享,并且没有安全问题

    转自:http://blog.chinaunix.net/uid-26363964-id-3049097.html

             https://www.cnblogs.com/cyw080/archive/2009/11/17/1604272.html

    更多相关内容
  • 多站点共享Session常见的作法有: •使用.net自动的状态服务(Asp.net State Service); •使用.net的Session数据库; •使用Memcached。 •使用Cookie方式实现多个站点间的共享(这种方式只限于几个站点都在同一...
  • NULL 博文链接:https://justcoding.iteye.com/blog/747398
  • NULL 博文链接:https://eleopard.iteye.com/blog/1767816
  • NULL 博文链接:https://xnbhnly.iteye.com/blog/2432706
  • Tomcat7共享session

    2018-04-09 17:33:12
    多个服务器部署Tomcat共享Session的方法,自己亲身测试
  • 基于maven构建的springboot工程,结合redis实现session共享的源码
  • NULL 博文链接:https://rainbow702.iteye.com/blog/1312307
  • 主要介绍了SpringBoot开发案例 分布式集群共享Session详解,在分布式系统中,为了提升系统性能,通常会对单体项目进行拆分,分解成多个基于功能的微服务,可能还会对单个微服务进行水平扩展,保证服务高可用,需要的...
  • shiro实现共享session;springboot集成redis实现共享session;集群环境下shiro共享session;shiro实现session共享

    shiro实现共享session;springboot集成redis共享session;集群环境下shiro共享session

    一、实现session共享

    1. 聊聊session共享

    如果是单机应用,session共享意义不大。使用默认的session缓存,还是存放到第三方应用缓存中,都可以。当然极端情况下,如果服务器内存非常小等极特殊情况下可能需要第三方缓存的。

    session共享是针对集群(或分布式、或分布式集群)的情况下采用;如果不做session共享,仍然采用默认的方式(session存放到默认的servlet容器),当我们的应用是以集群的方式发布的时候,同个用户的请求会被分发到不同的集群节点(分发依赖具体的负载均衡规则),那么每个处理同个用户请求的节点都会重新生成该用户的session,这些session之间是毫无关联的。那么同个用户的请求会被当成多个不同用户的请求,这肯定是不行的。

    2. shiro实现session共享(使用redis方式实现)

    实现共享session是比较简单的,换一种说明你就能明白。大家都会增、删、改、查,session的操作就是增删改查的过程,只不过默认是缓存到servlet容器中,咱们要将数据转移到redis,来实现它的增删改查。这样在集群环境中,大家都访问这个redis,也就实现了共享session.

    下面首先要了解下 shiro创建和缓存session的过程。

    1. DefaultWebSessionManager.java
      这个类是shiro的session管理类,我们在管理session生命周期时候,也该从这入手。
    2. AbstractSessionDAO.java
      这个类是session创建、保存、删除、更新操作的代码,默认会保存的servlet容器中。我们只需要继承这个类,并覆写对应代码即可实现session从redis中增删改查。下面来看看AbstractSessionDAO类官方的源码,其实也是增删改查:
    
    protected abstract Serializable doCreate(Session session);
    
    public void delete(Session session) {
            uncache(session);
            doDelete(session);
        }
    
    public void update(Session session) throws UnknownSessionException {
            doUpdate(session);
            if (session instanceof ValidatingSession) {
                if (((ValidatingSession) session).isValid()) {
                    cache(session, session.getId());
                } else {
                    uncache(session);
                }
            } else {
                cache(session, session.getId());
            }
        }
    
    protected abstract Session doReadSession(Serializable sessionId);
    

    从源码中可以看出,共享sessionId说白了就是改变增删改查保存的位置。
    默认session是保存的servlet缓存中,进行增删改查,现在咱们覆写方法,把增删改查的数据源改为redis。

    1. 新建实现类RedisSessionDao,并继承AbstractSessionDAO
    import org.apache.shiro.session.Session;
    import org.apache.shiro.session.UnknownSessionException;
    import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
    import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Component;
    import java.io.Serializable;
    import java.util.Collection;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @Author wangy
     * @create 2022/3/19 13:47
     * @Description  实现sessionDao,从而将session信息保存到redis中,达到集群环境共享session目的
     */
    @Component
    public class RedisSessionDao extends AbstractSessionDAO {
        /**
         * Session超时时间,单位为毫秒 当前设置半个小时
         */
        private long expireTime = 1800000;
    
        /**
         * 注入Redis操作类
         */
        @Autowired
        private RedisTemplate redisTemplate;
    
        /**
         * 获取活跃的session,可以用来统计在线人数,如果要实现这个功能,可以在将session加入redis时指定一个session前缀,统计的时候则使用keys("session-prefix*")的方式来模糊查找redis中所有的session集合
         * @return
         */
        @Override
        public Collection<Session> getActiveSessions() {
            return redisTemplate.keys("*");
        }
    
        /**
         * 新增和保存session到redis中
         * @param session
         * @return
         */
        @Override
        protected Serializable doCreate(Session session) {
            Serializable sessionId = this.generateSessionId(session);
            this.assignSessionId(session, sessionId);
    
            redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);
            return sessionId;
        }
    
        /**
         * 读取redis中的sessioin
         * @param sessionId
         * @return
         */
        @Override
        protected Session doReadSession(Serializable sessionId) {
            if (sessionId == null) {
                return null;
            }
            Session session = (Session) redisTemplate.opsForValue().get(sessionId);
            return session;
        }
    
        /**
         * 用户请求接口,然后修改session的有效期
         * @param session
         */
        @Override
        public void update(Session session) throws UnknownSessionException {
            if (session == null || session.getId() == null) {
                return;
            }
            //设置超时时间,这个是毫秒
            session.setTimeout(expireTime);
            redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);
        }
        /**
         * session到期后删除session,比如说退出登录 logout
         * @param session
         */
        @Override
        public void delete(Session session) {
            if (null == session) {
                return;
            }
            redisTemplate.opsForValue().getOperations().delete(session.getId());
        }
    }
    

    这个类的代码中实现了对session的增删改查操作,大家应该到这比较容易理解了。

    1. 最后将实现类应用到DefaultWebSessionManager中。
      在自己的ShiroConfig配置类型,如下代码:
    	@Bean
        public DefaultWebSecurityManager securityManager(RedisCachingSessionDao redisCachingSessionDao) {
            //新建security并设置realm、SessionManager
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(new AdminAuthorizingRealm());
            //新建SessionManager并设置SessionDao(session的获取途径)
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            sessionManager.setSessionDAO(redisCachingSessionDao);
            //应用sessionManager
            securityManager.setSessionManager(sessionManager);
            return securityManager;
        }
    

    将自己的实现类redisCachingSessionDao,通过代码set到sessionManager中。
    这样自己创建session后,可以从redis中查到自己的session数据了,共享session完成了,简单吧!

    3. 共享缓存实现

    共享session已经实现了,集群环境中能够访问相同的session数据源,但是shiro仍会有一些数据会缓存在servlet容器中,这样集群环境会出现一些其他的shiro配置数据各自用的还是各自的,出现各种各样问题。所以后面还需要解决 共享缓存的问题。
    用到下面一些类:

    1. CacheManager.java
      这个类是shiro的缓存接口,默认会使用servlet容器的来充当缓存。
      我们看下源码,这个类定义了获取缓存的方法。
    package org.apache.shiro.cache;
    
    public interface CacheManager {
        <K, V> Cache<K, V> getCache(String var1) throws CacheException;
    }
    
    1. Cache.java
      这个类是获取缓存方式的,通过getCache得到Cache的实现类,默认获取到的是shiro自己容器中,看下源码分析:
    public interface Cache<K, V> {
        V get(K var1) throws CacheException;
    
        V put(K var1, V var2) throws CacheException;
    
        V remove(K var1) throws CacheException;
    
        void clear() throws CacheException;
    
        int size();
    
        Set<K> keys();
    
        Collection<V> values();
    }
    

    同样道理,咱们继承这个类,并覆写这个类的增删改查,从缓存到servlet,转移到redis中。

    下面咱们定义自己的实现类
    3. ShiroRedisCache

    import org.apache.shiro.cache.Cache;
    import org.apache.shiro.cache.CacheException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Component;
    
    import java.util.Collection;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    /**
     * @Author wangy
     * @create 2022/3/19 11:55
     * @Description 缓存的实现类 shiro所有的缓存数据,都会存到redis中
     */
    @Component
    public class ShiroRedisCache<K,V> implements Cache<K,V>{
    
        /**
         * redis操作类
         */
        @Autowired
        private RedisTemplate<K,V> redisTemplate;
    
        /**
         * 定义缓存生效时间 为半个小时
         */
        private long expireTime = 1800;
    
        /**
         * 查询 操作
         */
        @Override
        public V get(K k) throws CacheException {
            return redisTemplate.opsForValue().get(k);
        }
    
        /**
         * 新增 操作
         */
        @Override
        public V put(K k, V v) throws CacheException {
            redisTemplate.opsForValue().set(k,v,expireTime, TimeUnit.SECONDS);
            return null;
        }
    
        /**
         * 删除 操作
         */
        @Override
        public V remove(K k) throws CacheException {
            V v = redisTemplate.opsForValue().get(k);
            redisTemplate.opsForValue().getOperations().delete(k);
            return v;
        }
    
        @Override
        public void clear() throws CacheException {
        }
    
        @Override
        public int size() {
            return 0;
        }
    
        @Override
        public Set<K> keys() {
            return null;
        }
    
        @Override
        public Collection<V> values() {
            return null;
        }
    }
    

    然后再次继承CacheManger,实现getCache方法
    4. ShiroRedisCacheManager

    import org.apache.shiro.cache.Cache;
    import org.apache.shiro.cache.CacheException;
    import org.apache.shiro.cache.CacheManager;
    import org.springframework.stereotype.Component;
    import javax.annotation.Resource;
    
    /**
     1. @Author wangy
     2. @create 2022/3/19 11:52
     3. @Description 实现缓存管理manager,所有缓存从redis中取数据
     */
    @Component
    public class ShiroRedisCacheManager implements CacheManager {
        /**
         * 注入自己的redisCache
         */
        @Resource
        private Cache shiroRedisCache;
    
        /**
         * 覆写方法
         */
        @Override
        public <K, V> Cache<K, V> getCache(String s) throws CacheException {
            return shiroRedisCache;
        }
    }
    

    最后将两个类set到Shrio配置中。
    4. set到shiro配置类中

    @Bean
        public DefaultWebSecurityManager securityManager(RedisCachingSessionDao redisCahingSessionDao, ShiroRedisCacheManager shiroRedisCacheManager) {
            //新建security并设置realm、CacheManager、SessionManager
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(new AdminAuthorizingRealm());
            //如果使用redis共享session,这个必须设置,因为集群之中 session要共享,同样一些缓存的数据也要共享,比如shiro缓存的数据
            securityManager.setCacheManager(shiroRedisCacheManager);
            //新建SessionManager并设置SessionDao(session的获取途径)
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            sessionManager.setSessionDAO(redisCahingSessionDao);
            securityManager.setSessionManager(sessionManager);
            return securityManager;
        }
    

    上面代码通过securityManager设置cacheManager属性来使用redis缓存方式。

    4. 总结

    1. 集群环境中shiro实现完共享session缓存,同样也要实现共享缓存,才能保证系统完美运行。
    2. 共享缓存不一定要用redis,大家通过覆写方法可以用mongodb、mysql、其他缓存工具等等都是可以实现的,当然要考虑效率和性能。
    3. 集群环境中负载均衡还可以通过ip_hash的机制将同个ip的请求定向到同一台后端,这样保证用户的请求始终是同一台服务处理,与单机应用基本一致了;但这有很多方面的缺陷(比如在同一个局域网环境下,都会分配到同一个ip,就失去负载均衡作用了)
      nginx负载均衡可以参考本人博文:https://blog.csdn.net/wangyue23com/article/details/108197650
    展开全文
  • 已经配置好,只需要自己更改redis的ip地址就可以使用,请测可以使用。
  • 自己亲自试验,亲测有效。实现父子网站共享session。可实现单点登录的效果,具体还是发挥各位大佬的才能了,此文只是实现共享session的亲测案例
  • 主要给大家介绍了关于android与asp.net服务端如何共享session的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋们下面随着小编来一起学习学习下吧。
  • 主要介绍了PHP简单实现HTTP和HTTPS跨域共享session解决办法,本文讲解的方法相对简单,需要的朋友可以参考下
  • nginx+tomcat实现负载均衡,共享session的两种方式: 1.使用Redis共享session 2.使用tomcat的组播功能。
  • Nginx负载均衡条件下Redis共享Session测试,实现负载均衡下两个服务器的项目可以共享Session
  • 主要介绍了实现Asp与Asp.Net共享Session的方法,需要的朋友可以参考下
  • session共享方案(tomcat8+redis共享session) 一步一步详细指引
  • 本插件适用tomcat8集成redis缓存。用于tomcat集群共享session之用。
  • springboot2.0.3 的redis共享session的demo,可以学习一下session共享
  • redis共享session

    2017-11-27 12:02:55
    redis共享session所需要的jar包,包括(commons-pool2-2.0.jar、jedis-2.6.0.jar、tomcat-redis-session-manager-sentinel-support-1.2.jar)
  • 主要为大家详细介绍了Springboot实现多服务器session共享,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 利用Nginx进行分布式部署,并使用redis实现session共享,https://blog.csdn.net/qq_34742298/article/details/80857173
  • 完整tomcat8内含session共享包,亲自测试可以使用,有问题可以私聊。
  • 主要介绍了解决asp.net负载均衡时Session共享的问题,详细的介绍了ASP.Net session存储方式以及如何实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • nginx实现session共享

    热门讨论 2017-07-20 09:52:36
    兼容Tomcat7 与tomcat8 +memcached做session共享 , 解决了tomcat7与Tomcat8的 java.lang.NoSuchFieldError: attributes 错误。
  • 本文实例讲述了Zend Framework实现多服务器共享SESSION数据的方法。分享给大家供大家参考,具体如下: 一、问题起源 大型网站通常有多个服务器,并且使用多个二级域名。这样一台服务器产生的session就不能为所有...
  • 主要介绍了SpringBoot中实现分布式的Session共享,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • shiro框架---多项目登录访问共享session的实现

    万次阅读 多人点赞 2018-08-04 17:55:57
    shiro框架—多项目登录访问共享session的实现 公司需要这样的需求:   有两个项目master 主项目、suiteone 项目,两个项目各自由shiro 安全框架管理,当不能登录时,都无法访问,但当登录了其中一个,再访问另...

    shiro框架—多项目登录访问共享session的实现

    公司需要这样的需求:

      有两个项目master 主项目、suiteone 项目,两个项目各自由shiro 安全框架管理,当不能登录时,都无法访问,但当登录了其中一个,再访问另一个的时候不再需要登录即可访问。
      如果想看为什么需要共享session ,可以去看我这篇文章。shiro框架—关于多项目之间验证为什么需要共享session

    关于实现多项目共享session的逻辑介绍

      其实在上边的链接里我已经说明了,但是怕大家不去看,所以我就又复制到了这里:
    先来说一下我的理解 ,如下:

    这里写图片描述
      上图中master 项目为主项目,登录页即在这个项目中,suiteonesuitetwo 为两个从项目,当两个从项目有请求时,如果没有登录的时候,都会打到master 项目的登录页上。共享session 采用的是redis 存储。

    上图的步骤如下:

    1. 浏览器请求master 项目,第一次请求的时候,也是会带着浏览器中的cookie 去请求,当然第一次去redis 里肯定找不到对应的session,会通过⑤进入到登录页。
    2. 当在登录页输入完正确的账号密码后,才能登录成功,否则仍会回到⑤。
    3. 在这一步的时候,会将登录成功后的session ,根据它,将生成sessionId串 ,并传到前端浏览器中,浏览器以cookie 存储。
    4. 同时将第③步中生成的session 存储到redis 中。
    5. 当前这里,不只是当登录失败的时候,会进入到登录页中,当浏览器长时间没有访问后台(每次浏览器访问后台,其实都会刷新session 的过期时间expireTime),导致session 超过时,也会进入到该步中。
    6. 当浏览器请求suiteonesuteTwo 这两个从项目时,肯定也是将当前浏览器中的所有的cookie 设置到request headers 请求头中。
    7. 根据传入的sessionId串 到共享的redis 存储中匹配。
    8. 如果匹配不到,则会跳转到master 项目的登录页,如果匹配成功,则会访问通过。

      以上描述的并不难,大家也都会想到,那么如何将扯了这么多的淡 真正更简单的,落地实现才是大家关注的。

    多项目通过redis共享session的步骤

      关于该项目已在GitHub上传,有兴趣的可以下载下来瞧瞧,GitHub项目地址

    1、shiro 共享session 的切入点,下图是shiro的验证流程

    这里写图片描述

    2、配置过程

      说一下实现shiro 项目共享session 的配置步骤:

    (1)项目多添加redis 依赖。
    <dependency>
        <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-redis</artifactId>
         <version>1.3.6.RELEASE</version>
     </dependency>
    
    (2)配置application.properties 文件
    ###主项目写成自己项目的登录页面路径,从项目必须写完整的主项目登录页面url
    shiro.loginUrl=/login
    ###主从项目的下边名字必须一致
    shiro.jessionid=sessionId
    ###redis连接配置
    spring.redis.host=192.168.1.160
    spring.redis.port=6379
    spring.redis.password=znxd
    
    (3)重写session 增删改查,与redis接入
    package microservice.sc.shiro;
    
    import org.apache.shiro.session.Session;
    import org.apache.shiro.session.UnknownSessionException;
    import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    import java.io.Serializable;
    import java.util.Collection;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by Administrator on 2018/7/31.
     */
    @Service
    public class RedisSessionDao extends AbstractSessionDAO {
        // Session超时时间,单位为毫秒
        private long expireTime = 120000;
    
        @Autowired
        private RedisTemplate redisTemplate;// Redis操作类,对这个使用不熟悉的,可以参考前面的博客
    
        public RedisSessionDao() {
            super();
        }
    
        public RedisSessionDao(long expireTime, RedisTemplate redisTemplate) {
            super();
            this.expireTime = expireTime;
            this.redisTemplate = redisTemplate;
        }
    
        @Override // 更新session
        public void update(Session session) throws UnknownSessionException {
            System.out.println("===============update================");
            if (session == null || session.getId() == null) {
                return;
            }
            session.setTimeout(expireTime);
            redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);
        }
    
        @Override // 删除session
        public void delete(Session session) {
            System.out.println("===============delete================");
            if (null == session) {
                return;
            }
            redisTemplate.opsForValue().getOperations().delete(session.getId());
        }
    
        @Override
    // 获取活跃的session,可以用来统计在线人数,如果要实现这个功能,可以在将session加入redis时指定一个session前缀,统计的时候则使用keys("session-prefix*")的方式来模糊查找redis中所有的session集合
        public Collection<Session> getActiveSessions() {
            System.out.println("==============getActiveSessions=================");
            return redisTemplate.keys("*");
        }
    
        @Override// 加入session
        protected Serializable doCreate(Session session) {
            System.out.println("===============doCreate================");
            Serializable sessionId = this.generateSessionId(session);
            this.assignSessionId(session, sessionId);
    
            redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);
            return sessionId;
        }
    
        @Override// 读取session
        protected Session doReadSession(Serializable sessionId) {
            System.out.println("==============doReadSession=================");
            if (sessionId == null) {
                return null;
            }
            return (Session) redisTemplate.opsForValue().get(sessionId);
        }
    
        public long getExpireTime() {
            return expireTime;
        }
    
        public void setExpireTime(long expireTime) {
            this.expireTime = expireTime;
        }
    
        public RedisTemplate getRedisTemplate() {
            return redisTemplate;
        }
    
        public void setRedisTemplate(RedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
    
        }
    }
    
    
    (4)将重写的RedisSessionDao 接入到shiro 中的sessionManager
    @Bean
    	public RedisSessionDao getRedisSessionDao(){
    		return new RedisSessionDao();
    	}
    	/**
    	 * @see DefaultWebSessionManager
    	 * @return
    	 */
    	@Bean(name="sessionManager")
    	public DefaultWebSessionManager defaultWebSessionManager() {
    		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    		//sessionManager.setCacheManager(cacheManager());
    		sessionManager.setGlobalSessionTimeout(43200000); //12小时
    		sessionManager.setDeleteInvalidSessions(true);
    		//关键在这里
    		sessionManager.setSessionDAO(getRedisSessionDao());
    		sessionManager.setSessionValidationSchedulerEnabled(true);
    		sessionManager.setDeleteInvalidSessions(true);
    		sessionManager.setSessionIdCookie(getSessionIdCookie());
    		return sessionManager;
    	}
    
    (5)将sessionManager 注入到securityManager
    /**
    	 * @see org.apache.shiro.mgt.SecurityManager
    	 * @return
    	 */
    	@Bean(name="securityManager")
    	public DefaultWebSecurityManager securityManager() {
    		DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
    		manager.setRealm(userRealm());
    		//manager.setCacheManager(cacheManager());
    		manager.setSessionManager(defaultWebSessionManager());
    		return manager;
    	}
    
    (6)设置登录页的地址cookie名字

      首先引入我们刚才application.properties文件中的内容:

    @Value("${shiro.loginUrl}")
    private String masterLoginUrl;
    
    @Value("${shiro.jessionid}")
    private String jessionId;
    

      然后注入到配置中:

    	@Bean(name = "shiroFilter")
    	public ShiroFilterFactoryBean shiroFilter(){
    		ShiroFilterFactoryBean bean = new MShiroFilterFactoryBean(); //指向自定义过滤器,自定义过滤器对js/css等忽略
    		bean.setSecurityManager(securityManager());
    		//在这里设置登录页
    		bean.setLoginUrl(masterLoginUrl);
    		Map<String, Filter>filters = new LinkedHashMap<>();
    		filters.put("anon", new AnonymousFilter());
    		bean.setFilters(filters);
    		//shiro配置过滤规则少量的话可以用hashMap,数量多了要用LinkedHashMap,保证有序,原因未知
    		Map<String, String> chains =  new LinkedHashMap<>();
    		chains.put("/login","anon");
    		chains.put("/loginForm","anon");
    		chains.put("/**", "authc");
    		bean.setFilterChainDefinitionMap(chains);
    		return bean;
    	}
    
    /**
    	 * 给shiro的sessionId默认的JSSESSIONID名字改掉
    	 * @return
    	 */
    	@Bean(name="sessionIdCookie")
    	public SimpleCookie getSessionIdCookie(){
    		SimpleCookie simpleCookie = new SimpleCookie(jessionId);
    		return simpleCookie;
    	}
    

      注意,如果值注入不进来,则看一下当前的shiro 配置文件里的LifecycleBeanPostProcessor 的注入是否为static 方法,如下:

    /**
    	 * 该类如果不设置为static,@Value注解就无效,原因未知
    	 * @return
    	 */
    	@Bean
    	public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    		return new LifecycleBeanPostProcessor();
    	}
    
    (7)测试

      我的项目如下:
      如下图所示,我们是springboot+dubbo+zookeeper 的项目:
    这里写图片描述
    分别启动30000端口 的主项目master 项目、300001端口的从项目suiteone 项目,启动后,

    • 首先访问主项目http://localhost:30000肯定进入登录页。
    • 当再访问http://localhost:30001/test ,因为没有登录直接被重定向到http://localhost:30000 的登录页上,即主项目的登录页。
    • 当在当前登录页上登录成功后,再次访问http://localhost:30001/test ,该接口即返回了数值。
    • 当我在主项目的http://localhost:30000中退出登录,再次访问http://localhost:30001/test ,同样重定向了登录页。
    • 应该能确定实现了session 共享。

    具体如下图:
      登录页面,在这时访问30001 也会进入下边这个页面:
    这里写图片描述
      登录成功后:
    这里写图片描述
      登录成功后访问30001的接口,即可以访问成功
    这里写图片描述

    配置完成,如有问题请及时留言。

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 264,208
精华内容 105,683
关键字:

共享session

友情链接: Line__phase.zip