1. 程式人生 > >spring+shiro+reids+ehcache實現session管理

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

相關推薦

spring+shiro+reids+ehcache實現session管理

前兩天,在專案中遇到需要對session管理的 一個需求,查詢了各種資料,也遇到了各種問題,不過,最後還是實現了需求,在此,也總結一下實現的過程,方便以後查閱。文中借鑑了很多其他文章內容,如有不當敬請諒解,一切以學習為主。 專案需求 統計檢視線上人數

spring-shiro-reids 叢集採用redis做session儲存

package com.iss.rdp.extension.web.shiro.cluster; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import

Spring Session實現Session管理的原理與詳細配置

原理請參考這篇文章很詳細:點選檢視 以下是我的在開發中使用配置 如果是maven工程,加入如下依賴: <!-- spring-session --> <dependency> <groupId>

細說shiro之六:session管理

log sub href del 通過 tin image pre 整合 官網:https://shiro.apache.org/ - org.apache.shiro.session.Session - org.apache.shiro.session.mgt

spring系統學習--spirngMVC之session管理

   繼續把spirngmvc的關於session管理的筆記記一下。 第一步:  新建相關處理邏輯的控制器: package com.automannn.springMVC_practice.controller; import org.springframe

spring boot 使用ehcache 實現快取

所謂快取是一種儲存機制,可將資料儲存在某一個地方,並以一種更快的方式為以後的請求提供服務 spring 對於快取提供了宣告式快取註解 ,並提供了四種類型的宣告式快取註解(同樣是使用了AOP 技術實現),這些註解定義了那些 方法的返回值將要被快取或者從快取儲存器中移除 ,需要注意的是,

shiro框架之六-------session管理

官網:https://shiro.apache.org/ 我們先來看一下shiro中關於Session和Session Manager的類圖。 如上圖所示,shiro自己定義了一個新的Session介面,用於統一操作介面,並通過SessionManager實現Session管理。 其中的3

shiro-shiroFilter,Realm實現,會話管理,securityManager

大家推薦個靠譜的公眾號程式設計師探索之路,大家一起加油,這個公眾號已經接入圖靈 ​   最近一個小夥伴需要用到shiro我就順變弄了一份,還有很多需要完善的地方,以後慢慢完善。目前可以實現使用者角色,許可權,自定義realm,會話管理的功能 shiro配置檔案 &

spring boot 整合 EHcache 實現本地快取

需要的依賴如下,pom檔案新增 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-

shiro學習分享(四)——session管理以及單使用者登陸限制(網頁應用版)

session管理以及併發使用者登陸限制(網頁應用版) 首先講一下博主在shiro中對session和cookie使用的理解吧。 在每一次使用者成功登陸後,shiro都會自動的建立一個session並儲存在服務端,該session會包含Subject的基本資訊

Spring Boot 整合 log4j2 實現日誌管理

摘要:上一篇,我們講了Spring Boot 整合 log4j實現日誌管理,這一篇接著說一下Spring Boot 整合 log4j2,。 一:還是新建一個java工程: 二:增加log4j2的pom.xml配置,如下: <project xmlns="htt

shiro 攔截器實現session過期攔截ajax請求的處理

攔截器程式碼: package com.xlqh.outlook.shirofilter; import java.io.IOException; import org.apache.shiro.

spring security 在沒實現session共享的叢集環境下 防止使用者多次登入的 實現思路

背景 專案採用阿里雲負載均衡,基於cookie的會話保持。 沒有實現叢集間的session共享。 專案採用spring security 並且配置了session策略如下: <bean class="or

Spring Boot整合Druid實現資料來源管理與監控

1. 引言 在程式設計師的日常工作中, 經常需要編寫資料庫操作相關的程式,而這就需要資料連線池中介軟體用於管理資料庫連線。資料庫連線池負責分配、管理和釋放資料庫連線,它允許應用程式重複使用一個現有的資料庫連線,而不是再重新建立一個;釋放空閒時間超過最大空閒時間

Spring Boot 整合 log4j 實現日誌管理報錯:java.lang.IllegalArgumentException: LoggerFactory is not a Logback

問題: Caused by: java.lang.IllegalArgumentException: LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. Either remove Log

Spring Boot 整合 log4j 實現日誌管理

摘要:最近有時間,系統的學習了一下Spring Boot框架,感覺Spring Boot很好的集成了各種框架和元件,之前我們用Spring的時候,要配置好的依賴和xml檔案,現在使用Spring Boot,只需要一些少量的配置就可以實現。今天我們來看下Spring Boot

Spring AOP攔截Service實現日誌管理(自定義註解的方式)

最近專案中用到AOP方式進行Service操作方法日誌管理,特為之記! 1、先說理論和採用的方法 採用註解的方式,其中包括以下註解:@Aspect(類註解)和@AfterReturning(方法註解),其中需要用的Maven庫如下: "org.aspectj:aspect

android http請求實現session管理

session一般儲存在Cookie當中,首先我們瞭解一下session和cookie Cookie和Session都為了用來儲存狀態資訊,都是儲存客戶端狀態的機制,它們都是為了解決HTTP無狀態的問題而所做的努力。 Session可以用Cookie來實現,也可

spring-boot整合ehcache實現快取機制

EhCache 是一個純Java的程序內快取框架,具有快速、精幹等特點,是Hibernate中預設的CacheProvider。 ehcache提供了多種快取策略,主要分為記憶體和磁碟兩級,所以無需擔心容量問題。 spring-boot是一個快速的整合框架,其設計目的

008-shirospring web項目整合【二】認證、授權、session管理

添加 ner != efi ebs ref private date err 一、認證 1、添加憑證匹配器 添加憑證匹配器實現md5加密校驗。 修改applicationContext-shiro.xml: <!-- realm -->