spring+shiro+reids+ehcache实现session管理

前两天,在项目中遇到需要对session管理的 一个需求,查询了各种资料,也遇到了各种问题,不过,最后还是实现了需求,在此,也总结一下实现的过程,方便以后查阅。文中借鉴了很多其他文章内容,如有不当敬请谅解,一切以学习为主。

项目需求

  • 统计查看在线人数
  • 控制在线人数,管理员可下线某在线用户
  • 控制用户在线时长
  • 保证同一用户只能在同一客户端登录

    基于需求分析,考虑使用redis缓存session的方案:
    1.使用redis可方便的设置缓存的有限期,这样可控制用户的在线时长问题。用户登录,前端操作均会更新缓存数据。
    2.redis性能毋庸置疑,同时,redis的java api也能方便操作redis中的数据

项目依赖

<properties>
    <!-- 主要依赖库的版本定义 -->
    <springside.version>4.2.3-GA</springside.version>
    <spring.version>4.0.5.RELEASE</spring.version>
    <shiro.version>1.2.3</shiro.version>
</properties>

<dependency>
    <groupId>org.springside</groupId>
    <artifactId>springside-core</artifactId>
    <version>${springside.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>${shiro.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>${shiro.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
          <artifactId>spring-session-data-redis</artifactId>
          <version>1.1.1.RELEASE</version>
          <type>pom</type>
</dependency
<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>2.4.6</version>
</dependency>

web.xml配置filter,加载shiro和其他配置文件

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath*:/applicationContext.xml,
            classpath*:/applicationContext-shiro.xml,
            classpath*:/applicationContext-session.xml
        </param-value>
    </context-param>

    <filter>  
        <filter-name>shiroFilter</filter-name>  
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
         <init-param>
              <param-name>targetFilterLifecycle</param-name>
              <param-value>true</param-value>
          </init-param>  
    </filter>  
    <filter-mapping>  
        <filter-name>shiroFilter</filter-name>  
        <url-pattern>/*</url-pattern> 
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
        <dispatcher>ERROR</dispatcher> 
    </filter-mapping>  

shiro配置文件applicationContext-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"
    default-lazy-init="true">

    <description>Shiro安全配置</description>
    <!-- 項目自定义的Realm -->
    <bean id="shiroRealm" class="com.sinosoft.bi.base.security.ShiroRealm"></bean>

    <!-- Shiro's main business-tier object for web-enabled applications -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="shiroRealm" />
        <property name="sessionManager" ref="sessionManager"></property>
        <property name="cacheManager" ref="shiroEhcacheManager" />
    </bean>
    <!-- Shiro Filter -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/login" />
        <property name="successUrl" value="/" />
        <property name="unauthorizedUrl" value="/unauthorized" />
        <property name="filterChainDefinitions">
            <value>
                /login = authc
                /logout = logout
                /static/** = anon
                /admin/** = roles[admin]
                /** = user
            </value>
        </property>
    </bean>

    <!-- 用户授权信息Cache, 采用EhCache -->
    <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache/ehcache-shiro.xml" />
    </bean>

    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

    <!--开启Shiro的注解-->
    <!-- <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>

    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean> -->

    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">  
        <property name="sessionDAO" ref="sessionDao"></property>  
        <property name="globalSessionTimeout" value="60000" />
        <property name="deleteInvalidSessions" value="true"></property>
        <!-- 删除失效session -->  
        <property name="sessionValidationSchedulerEnabled" value="true" />  
        <property name="sessionListeners" ref="myShiroSessionListener"></property>
        <!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID -->
        <!-- <property name="sessionIdCookie" ref="sharesession" />  -->
    </bean>

     <!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID -->
    <!-- <bean id="sharesession" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg name="name" value="SHAREJSESSIONID" />
        <property name="path" value="/" />
        <property name="httpOnly" value="true"/>
    </bean> -->

    <!-- Session ID 生成器 -->
    <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"></bean>

    <!-- session 监听器 -->
    <bean id="myShiroSessionListener" class="com.sinosoft.bi.base.security.MyShiroSessionListener"></bean>  

    <bean id="sessionDao" class="com.sinosoft.bi.base.security.SessionDao">  
        <property name="redisUtil" ref="redisUtil"></property>
        <property name="sessionIdGenerator" ref="sessionIdGenerator"></property>
    </bean> 
</beans>

applicationContext-session.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"
    default-lazy-init="true">

    <!-- redis连接池配置-->    
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig" >    
        <!--最大空闲数-->    
        <property name="maxIdle" value="${redis.maxIdle}" />    
        <!--连接池的最大数据库连接数  -->  
        <property name="maxTotal" value="${redis.maxTotal}" />  
        <!--最大建立连接等待时间-->    
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />    
        <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟)-->  
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" />   
        <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3-->  
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" />   
        <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1-->  
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" />   
        <!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个-->    
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />    
        <!--在空闲时检查有效性, 默认false  -->  
        <property name="testWhileIdle" value="${redis.testWhileIdle}" />    
    </bean >  


    <!-- redis集群配置 哨兵模式 -->  
    <!-- <bean id="sentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">  
        <property name="master">  
            <bean class="org.springframework.data.redis.connection.RedisNode">  
                这个值要和Sentinel中指定的master的值一致,不然启动时找不到Sentinel会报错的  
                <property name="name" value="mymaster"></property>  
            </bean>  
        </property>  
        记住了,这里是指定Sentinel的IP和端口,不是Master和Slave的  
        <property name="sentinels">  
            <set>  
                <bean class="org.springframework.data.redis.connection.RedisNode">  
                    <constructor-arg name="host" value="${redis.sentinel.host1}"></constructor-arg>  
                    <constructor-arg name="port" value="${redis.sentinel.port1}"></constructor-arg>  
                </bean>  
                <bean class="org.springframework.data.redis.connection.RedisNode">  
                    <constructor-arg name="host" value="${redis.sentinel.host2}"></constructor-arg>  
                    <constructor-arg name="port" value="${redis.sentinel.port2}"></constructor-arg>  
                </bean>  
                <bean class="org.springframework.data.redis.connection.RedisNode">  
                    <constructor-arg name="host" value="${redis.sentinel.host3}"></constructor-arg>  
                    <constructor-arg name="port" value="${redis.sentinel.port3}"></constructor-arg>  
                </bean>  
            </set>  
        </property>  
    </bean>  
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">  
        <constructor-arg name="sentinelConfig" ref="sentinelConfiguration"></constructor-arg>  
        <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>  
    </bean> -->  

    <!--redis连接工厂 -->  
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">   
        <property name="poolConfig" ref="jedisPoolConfig"></property>  

       <!--  <property name="hostName" value="${redis.hostName}"></property>   
        <property name="port" value="${redis.port}"></property>    -->

         <property name="hostName" value="127.0.0.1"></property>   
        <!--端口号  -->  
        <property name="port" value="6379"></property> 
        <!--如果Redis设置有密码  -->  
       <!--  <property name="password" value="${redis.password}" />   -->
        <!--客户端超时时间单位是毫秒  -->  
        <property name="timeout" value="${redis.timeout}"></property>   
    </bean>    

    <!--redis操作模版,使用该对象可以操作redis  -->  
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >    
        <property name="connectionFactory" ref="jedisConnectionFactory" />    
        <!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!! 这里配置之前启动时,遇到的坑比较大,序列化之后,反序列化出现了问题,报了一个异常,找寻很久,才发现是这里的配置出现了问题 -->    
        <property name="keySerializer" >    
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />    
        </property>    
        <property name="valueSerializer" >    
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
        </property>    
        <property name="hashKeySerializer">    
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>    
        </property>    
        <property name="hashValueSerializer">    
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>    
        </property>    
        <!--开启事务  -->  
        <property name="enableTransactionSupport" value="true"></property>  
    </bean >      

</beans>  

redis配置文件redis.properties

redis.hostName=127.0.0.1 
redis.port=6379
#redis.password=123456
redis.timeout=10000  
redis.maxIdle=300  
redis.maxTotal=1000  
redis.maxWaitMillis=1000  
redis.minEvictableIdleTimeMillis=300000  
redis.numTestsPerEvictionRun=1024  
redis.timeBetweenEvictionRunsMillis=30000  
redis.testOnBorrow=true  
redis.testWhileIdle=true

ehcache-shiro.xml

<ehcache updateCheck="false" name="shiroCache">
   <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />
</ehcache>

User实体

public class User implements Serializable{
    private static final long serialVersionUID = 1L;
    private String userID;
    private String username;
    private String password;
    ...getter and setter
}

序列化和反序列化工具

public class SerializeUtils {

    /**
     * 序列化
     * 
     * @param object
     * @return
     * @throws Exception
     */
    public static byte[] serialize(Object object) throws Exception {
        if(object == null) return null;
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        try {
            // 序列化
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            byte[] bytes = baos.toByteArray();
            return bytes;
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 反序列化
     * 
     * @param bytes
     * @return
     * @throws Exception
     */
    public static Object unSerialize(byte[] bytes) throws Exception {
        if(bytes == null) return null;
        ByteArrayInputStream bais = null;
        try {
            // 反序列化
            bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }


    // 把session对象转化为byte保存到redis中  
    public static byte[] sessionToByte(Session session){  
        ByteArrayOutputStream bo = null; 
        byte[] bytes = null;
        ObjectOutput oo = null;
        try {
            bo = new ByteArrayOutputStream();  
            oo = new ObjectOutputStream(bo);  
            oo.writeObject(session);  
            bytes = bo.toByteArray();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }finally {
            try {
                if(null != oo){
                    oo.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                if(null != bo){
                    bo.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bytes;  
    }  

    // 把byte还原为session  
    public static Session byteToSession(byte[] bytes){  
        ObjectInputStream in = null;
        Session session = null;  
        try {  
            in = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(bytes)));    
            session = (Session) in.readObject();  
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  catch (Exception e) {
            e.printStackTrace();  
        }
        return session;  
    }  
}

redis工具类

/**
 * @author myj 
 *  基于spring和redis的redisTemplate工具类
 *  针对所有的hash 都是以h开头的方法 
 *  针对所有的Set  都是以s开头的方法 不含通用方法
 *  针对所有的List 都是以l开头的方法
 */

@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate<Serializable, Object> redisTemplate;

    public void setRedisTemplate(RedisTemplate<Serializable, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // =============================common============================
    /**
     * 指定缓存失效时间
     * 
     * @param key
     *            键
     * @param time
     *            时间(秒)
     * @return
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     * 
     * @param key
     *            键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     * 
     * @param key
     *            键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public List<Session> hmget(){
        List<Session> list = new ArrayList<Session>();
        List<Object> values = redisTemplate.boundHashOps(KEY).values();
        for (Object object : values) {
            list.add((Session) object);
        }
        return list;
    }


    public List<String> keys(){
      Set<Serializable> values =  redisTemplate.keys("*");
      List<String> list = new LinkedList<String>();
      for (Object object : values) {
        list.add((String)object);
      }
      return list;
    }

    /**
     * 删除缓存
     * 
     * @param key
     *            可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

     /** 
     * 删除对应的value 
     *  
     * @param key 
     */  
    public void remove(final String key) {  
        if (exists(key)) {  
            redisTemplate.delete(key);  
        }  
    }

    /** 
     * 判断缓存中是否有对应的value 
     *  
     * @param key 
     * @return 
     */  
    public boolean exists(final String key) {  
        return redisTemplate.hasKey(key);  
    }  


    /** 
     * 批量删除对应的value 
     * @param keys 
     */  
    public void remove(final String... keys) {  
        for (String key : keys) {  
            remove(key);  
        }  
    }

    /** 
     * 批量删除key 
     *  
     * @param pattern 
     */  
    public void removePattern(final String pattern) {  
        Set<Serializable> keys = redisTemplate.keys(pattern);  
        if (keys.size() > 0)  
            redisTemplate.delete(keys);  
    }


    // ============================String=============================

    /**
     * 普通缓存放入并设置时间
     * @param key
     *            键
     * @param value
     *            值
     * @param time
     *            时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


     /** 
     * 读取缓存 
     *  
     * @param key 
     * @return 
     */  
    public Object get(final String key) {  
        Object result = null;  
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();  
        result = operations.get(key);  
        return result;  
    }  

    /** 
     * 写入缓存 
     *  
     * @param key 
     * @param value 
     * @return 
     */  
    public boolean set(final String key, Object value) {  
        boolean result = false;  
        try {  
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();  
            operations.set(key, value);  
            result = true;  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return result;  
    }  

    /** 
     * 写入缓存 
     *  
     * @param key 
     * @param value 
     * @return 
     */  
    public boolean set(final String key, Object value, Long expireTime) {  
        boolean result = false;  
        try {  
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();  
            operations.set(key, value);  
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);  
            result = true;  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return result;  
    }  

    /**
     * 递增
     * 
     * @param key
     *            键
     * @param by
     *            要增加几(大于0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     * 
     * @param key
     *            键
     * @param by
     *            要减少几(小于0)
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    // ================================Map=================================
    /**
     * HashGet
     * 
     * @param key
     *            键 不能为null
     * @param item
     *            项 不能为null
     * @return 值
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     * 
     * @param key
     *            键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * 
     * @param key
     *            键
     * @param map
     *            对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * HashSet 并设置时间
     * 
     * @param key
     *            键
     * @param map
     *            对应多个键值
     * @param time
     *            时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * 
     * @param key
     *            键
     * @param item
     *            项
     * @param value
     *            值
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * 
     * @param key
     *            键
     * @param item
     *            项
     * @param value
     *            值
     * @param time
     *            时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的值
     * 
     * @param key
     *            键 不能为null
     * @param item
     *            项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断hash表中是否有该项的值
     * 
     * @param key
     *            键 不能为null
     * @param item
     *            项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     * 
     * @param key
     *            键
     * @param item
     *            项
     * @param by
     *            要增加几(大于0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递减
     * 
     * @param key
     *            键
     * @param item
     *            项
     * @param by
     *            要减少记(小于0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    // ============================set=============================
    /**
     * 根据key获取Set中的所有值
     * 
     * @param key
     *            键
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     * 
     * @param key
     *            键
     * @param values
     *            值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     * 
     * @param key
     *            键
     * @param time
     *            时间(秒)
     * @param values
     *            值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     * 
     * @param key
     *            键
     * @return
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值为value的
     * 
     * @param key
     *            键
     * @param values
     *            值 可以是多个
     * @return 移除的个数
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    // ===============================list=================================

    /**
     * 获取list缓存的内容
     * 
     * @param key
     *            键
     * @param start
     *            开始
     * @param end
     *            结束 0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     * 
     * @param key
     *            键
     * @return
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     * 
     * @param key
     *            键
     * @param index
     *            索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @param time
     *            时间(秒)
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @param time
     *            时间(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @param time
     *            时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @param time
     *            时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     * 
     * @param key
     *            键
     * @param index
     *            索引
     * @param value
     *            值
     * @return
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除N个值为value
     * 
     * @param key
     *            键
     * @param count
     *            移除多少个
     * @param value
     *            值
     * @return 移除的个数
     */
    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
}

MyShiroSessionListener

public class MyShiroSessionListener implements SessionListener {  

    @Autowired RedisUtil redisUtil;

    @Override  
    public void onStart(Session session) {  
    }  

    @Override  
    public void onStop(Session session) {  
        redisUtil.remove(session.getId().toString());  
    }  

    @Override  
    public void onExpiration(Session session) {  
        redisUtil.remove(session.getId().toString());  
    }  

}  

SessionDao

@Component
public class SessionDao extends EnterpriseCacheSessionDAO {  

    private RedisUtil redisUtil; 

    public RedisUtil getRedisUtil(){
        return redisUtil;  
    }  

    public void setRedisUtil(RedisUtil redisUtil) {  
        this.redisUtil = redisUtil;  
    }  

    // 创建session,保存到数据库  
    @Override  
    protected Serializable doCreate(Session session) { 
        Serializable sessionId = super.doCreate(session);
        redisUtil.set(session.getId().toString(), SerializeUtils.sessionToByte(session),1*60*30L);
        return sessionId;  
    }  

    // 获取session  
    @Override  
    protected Session doReadSession(Serializable sessionId) {  
        // 先从缓存中获取session,如果没有再去数据库中获取  
       Session session = super.doReadSession(sessionId);
       if(session == null){
            Object object = null;
            byte[] bytes = null;
            if(redisUtil.exists(sessionId.toString())){
                object = redisUtil.get(sessionId.toString());
            }
            if(null != object){
                bytes = (byte[])object;
            }
            if(bytes != null && bytes.length > 0){  
                session = (Session) SerializeUtils.byteToSession(bytes);
            }  
        }  
        return session;
    }  

    // 更新session的最后一次访问时间  
    @Override  
    protected void doUpdate(Session session) {
        super.doUpdate(session);  
        redisUtil.set(session.getId().toString(), SerializeUtils.sessionToByte(session),1*60*30L);
    }

    @Override
    public Collection<Session> getActiveSessions() {
        List<Session> list = redisUtil.hmget();
        return list;
    }

    // 删除session  
    @Override
    public void doDelete(Session session) {  
        super.doDelete(session);  
        redisUtil.remove(session.getId().toString());  
    }  


}  

Usercontroller部分代码

    /**
     * 下线某在线用户
     * @return
     */
    @RequestMapping(value = "/offline")
    @ResponseBody
    public String offline(){
        String sessionid = getParams().getString("id");
        byte[] bytes = null;
        Session session =  null;
        try{
            if(redisUtil.exists(sessionid.toString())){
                bytes = (byte[]) redisUtil.get(sessionid.toString());
            }
            if(bytes != null && bytes.length > 0){  
                session = SerializeUtils.byteToSession(bytes);
                if(session !=  null){
                    sessionDao.delete(session);
                }
            }  
            return "success";
        }catch(Exception e){
            e.printStackTrace();
            return "error";
        }
    }

    /**
     * 获取在线人数
     * @param model
     * @return
     */
    @RequestMapping(value = "/getUserOnline")
    public String getUserOnline(Model model){
        Map<String, User> onlineMap = null;
        Session session =  null;
        User user = null;
        byte[] bytes = null;
        try {
            List<String> list = redisUtil.keys();
            if(list.size() >0){
                onlineMap = new LinkedHashMap<String,User>();
                for (String key : list) {
                    if(redisUtil.exists(key.toString())){
                        bytes = (byte[]) redisUtil.get(key.toString());
                    }
                    if(bytes != null && bytes.length > 0){  
                        session = SerializeUtils.byteToSession(bytes);
                        if(session !=  null){
                            user = (User) session.getAttribute("currentUser");
                            if(user != null){
                                onlineMap.put(key, user);
                            }
                        }
                    }  
                }
            }
            model.addAttribute("onlineMap", onlineMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "base/user/online";
    }

spring容器启动监听,清楚redis中的缓存

/**
 * spring容器监听类
 * 容器重启时,清空redis缓存
 * @author mayi
 *
 */
@Component
public class MyContextRefreshedEvent implements ApplicationListener<ContextRefreshedEvent>{ 

    @Override  
    public void onApplicationEvent(ContextRefreshedEvent arg0) {  
        @SuppressWarnings("resource")
        JedisPool jedisPool = new JedisPool("127.0.0.1",6379);
        Jedis jedis = jedisPool.getResource();
        jedis.flushAll();
        jedis = null;
        jedisPool = null;
    }  

}  

猜你喜欢

转载自blog.csdn.net/qq_19635589/article/details/80077752
今日推荐