diff --git a/README.md b/README.md index 54ccc3c92cd0afdc89596f323005756c6bb9807e..6ca949c12564081a6ff6b2b46414265d24cd83c6 100644 --- a/README.md +++ b/README.md @@ -66,4 +66,49 @@ permission 拥有权限的用户
``` * 配置shiro与spring集成文件:spring-shiro.xml -* 页面中加入shiro标签 \ No newline at end of file +* 页面中加入shiro标签 + +## version: 0.0.3-RELEASE (release at 2017/10/27) +集成redis
+* 导入maven依赖 +```xml + + + redis.clients + jedis + 2.9.0 + + + org.springframework.data + spring-data-redis + 1.8.1.RELEASE + + +``` + +* 重写相关缓存类 +ExtJedisConnectionFactory.java
+RedisCache.java
+RedisCacheManager.java
+RedisManager.java
+RedisSessionDAO.java
+SerializeUtils.java
+ +* 修改相关配置 +增加spring-redis.xml
+spring-shiro.xml中添加sessionManager, 将cacheManager从Ecache换为RedisCacheManager
+shiro-demo.properties中增加redis相关配置 +```shell +######################## +## redis config +######################## +redis.config.maxTotal=50 +redis.config.maxIdle=10 +redis.config.minIdle=10 +redis.config.maxWaitMillis=1800 +redis.address=redis:6379 +redis.masterName= +redis.password= +redis.timeout=1800 +redis.expire=1800 +``` diff --git a/pom.xml b/pom.xml index 7e01bca6c619fdab87240d88edc5af571284d764..ecbc3ae3b14be94373687b092d12f1a88027dd17 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.cliveyuan shiro-demo war - 0.0.2-RELEASE + 0.0.3-RELEASE shiro-demo http://maven.apache.org @@ -16,6 +16,19 @@ + + + + redis.clients + jedis + 2.9.0 + + + org.springframework.data + spring-data-redis + 1.8.1.RELEASE + + diff --git a/src/main/java/ga/clive/demo/shiro/cache/ExtJedisConnectionFactory.java b/src/main/java/ga/clive/demo/shiro/cache/ExtJedisConnectionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..df973f5e5c0ab2c581a4ce9ed56ccb1a1b7ea233 --- /dev/null +++ b/src/main/java/ga/clive/demo/shiro/cache/ExtJedisConnectionFactory.java @@ -0,0 +1,44 @@ +package ga.clive.demo.shiro.cache; +import static org.springframework.util.Assert.notNull; + +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.util.StringUtils; + +public class ExtJedisConnectionFactory extends JedisConnectionFactory { + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + private String address; + + @Override + public void afterPropertiesSet() { + notNull(address, "address is 'null'."); + + String hostName = null; + Integer port = null; + String[] hostArray = address.split(","); + for (String host : hostArray) { + if (!StringUtils.isEmpty(host)) { + String[] ipAndPort = host.split(":"); + if (ipAndPort.length > 1) { + hostName = ipAndPort[0]; + port = Integer.parseInt(ipAndPort[1]); + break; + } + } + } + + notNull(hostName, "ip is 'null'."); + notNull(port, "port is 'null'."); + setHostName(hostName); + setPort(port); + + super.afterPropertiesSet(); + } + +} diff --git a/src/main/java/ga/clive/demo/shiro/cache/RedisCache.java b/src/main/java/ga/clive/demo/shiro/cache/RedisCache.java new file mode 100644 index 0000000000000000000000000000000000000000..b582be83ea7fca280c76b72be0103c3688ab8171 --- /dev/null +++ b/src/main/java/ga/clive/demo/shiro/cache/RedisCache.java @@ -0,0 +1,195 @@ +package ga.clive.demo.shiro.cache; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.apache.shiro.cache.Cache; +import org.apache.shiro.cache.CacheException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RedisCache implements Cache { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * The wrapped Jedis instance. + */ + private RedisManager cache; + + /** + * The Redis key prefix for the sessions + */ + private String keyPrefix = "shiro_redis_session:"; + + /** + * Returns the Redis session keys + * prefix. + * @return The prefix + */ + public String getKeyPrefix() { + return keyPrefix; + } + + /** + * Sets the Redis sessions key + * prefix. + * @param keyPrefix The prefix + */ + public void setKeyPrefix(String keyPrefix) { + this.keyPrefix = keyPrefix; + } + + /** + * 通过一个JedisManager实例构造RedisCache + */ + public RedisCache(RedisManager cache){ + if (cache == null) { + throw new IllegalArgumentException("Cache argument cannot be null."); + } + this.cache = cache; + } + + /** + * Constructs a cache instance with the specified + * Redis manager and using a custom key prefix. + * @param cache The cache manager instance + * @param prefix The Redis key prefix + */ + public RedisCache(RedisManager cache, + String prefix){ + + this( cache ); + + // set the prefix + this.keyPrefix = prefix; + } + + /** + * 获得byte[]型的key + * @param key + * @return + */ + private byte[] getByteKey(K key){ + if(key instanceof String){ + String preKey = this.keyPrefix + key; + return preKey.getBytes(); + }else{ + return SerializeUtils.serialize(key); + } + } + + @Override + public V get(K key) throws CacheException { + logger.debug("根据key从Redis中获取对象 key [" + key + "]"); + try { + if (key == null) { + return null; + }else{ + byte[] rawValue = cache.get(getByteKey(key)); + @SuppressWarnings("unchecked") + V value = (V)SerializeUtils.deserialize(rawValue); + return value; + } + } catch (Throwable t) { + throw new CacheException(t); + } + + } + + @Override + public V put(K key, V value) throws CacheException { + logger.debug("根据key从存储 key [" + key + "]"); + try { + cache.set(getByteKey(key), SerializeUtils.serialize(value)); + return value; + } catch (Throwable t) { + throw new CacheException(t); + } + } + + @Override + public V remove(K key) throws CacheException { + logger.debug("从redis中删除 key [" + key + "]"); + try { + V previous = get(key); + cache.del(getByteKey(key)); + return previous; + } catch (Throwable t) { + throw new CacheException(t); + } + } + + /** + * 非常危险的操作 + */ + @Override + @Deprecated + public void clear() throws CacheException { + logger.debug("从redis中删除所有元素"); +// try { +// cache.flushDB(); +// } catch (Throwable t) { +// throw new CacheException(t); +// } + } + + @Override + @Deprecated + public int size() { +// try { +// Long longSize = new Long(cache.dbSize()); +// return longSize.intValue(); +// } catch (Throwable t) { +// throw new CacheException(t); +// } + return 0; + } + + @SuppressWarnings("unchecked") + @Override + @Deprecated + public Set keys() { +// try { +// Set keys = cache.keys(this.keyPrefix + "*"); +// if (CollectionUtils.isEmpty(keys)) { +// return Collections.emptySet(); +// }else{ +// Set newKeys = new HashSet(); +// for(byte[] key:keys){ +// newKeys.add((K)key); +// } +// return newKeys; +// } +// } catch (Throwable t) { +// throw new CacheException(t); +// } + return new HashSet(); + } + + @Override + public Collection values() { +// try { +// Set keys = cache.keys(this.keyPrefix + "*"); +// if (!CollectionUtils.isEmpty(keys)) { +// List values = new ArrayList(keys.size()); +// for (byte[] key : keys) { +// @SuppressWarnings("unchecked") +// V value = get((K)key); +// if (value != null) { +// values.add(value); +// } +// } +// return Collections.unmodifiableList(values); +// } else { +// return Collections.emptyList(); +// } +// } catch (Throwable t) { +// throw new CacheException(t); +// } + return Collections.emptyList(); + } + +} diff --git a/src/main/java/ga/clive/demo/shiro/cache/RedisCacheManager.java b/src/main/java/ga/clive/demo/shiro/cache/RedisCacheManager.java new file mode 100644 index 0000000000000000000000000000000000000000..3569872e8a76de9790cfa0648ab3e80d4e3a43b5 --- /dev/null +++ b/src/main/java/ga/clive/demo/shiro/cache/RedisCacheManager.java @@ -0,0 +1,75 @@ +package ga.clive.demo.shiro.cache; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.shiro.cache.Cache; +import org.apache.shiro.cache.CacheException; +import org.apache.shiro.cache.CacheManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class RedisCacheManager implements CacheManager { + + private static final Logger logger = LoggerFactory + .getLogger(RedisCacheManager.class); + + // fast lookup by name map + private final ConcurrentMap caches = new ConcurrentHashMap(); + + private RedisManager redisManager; + + /** + * The Redis key prefix for caches + */ + private String keyPrefix = "shiro_redis_cache:"; + + /** + * Returns the Redis session keys + * prefix. + * @return The prefix + */ + public String getKeyPrefix() { + return keyPrefix; + } + + /** + * Sets the Redis sessions key + * prefix. + * @param keyPrefix The prefix + */ + public void setKeyPrefix(String keyPrefix) { + this.keyPrefix = keyPrefix; + } + + @Override + public Cache getCache(String name) throws CacheException { + logger.debug("获取名称为: " + name + " 的RedisCache实例"); + + Cache c = caches.get(name); + + if (c == null) { + + // initialize the Redis manager instance + //redisManager.init(); + + // create a new cache instance + c = new RedisCache(redisManager, keyPrefix); + + // add it to the cache collection + caches.put(name, c); + } + return c; + } + + public RedisManager getRedisManager() { + return redisManager; + } + + public void setRedisManager(RedisManager redisManager) { + this.redisManager = redisManager; + } + +} diff --git a/src/main/java/ga/clive/demo/shiro/cache/RedisManager.java b/src/main/java/ga/clive/demo/shiro/cache/RedisManager.java new file mode 100644 index 0000000000000000000000000000000000000000..e788e55c0ea4512804477e0a1a70398fed4b6c45 --- /dev/null +++ b/src/main/java/ga/clive/demo/shiro/cache/RedisManager.java @@ -0,0 +1,630 @@ +package ga.clive.demo.shiro.cache; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ga.clive.demo.shiro.util.StrUtils; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; +import redis.clients.jedis.JedisSentinelPool; +import redis.clients.jedis.Protocol; +import redis.clients.jedis.Response; +import redis.clients.jedis.Transaction; +import redis.clients.util.Pool; + +/** + *
+ *
Title: redis管理
+ *
Description:
+ *
+ * + * @author clive + * @version 1.0 + * @date 2017-10-26 17:50:51 + * + */ +public class RedisManager { + + Logger logger = LoggerFactory.getLogger(this.getClass()); + + // "192.168.100.224:26379,192.168.100.224:26379" + private String address = "192.168.100.224:26379,192.168.100.225:26379,192.168.100.226:26379"; + + private String masterName; + private String password; + + /** 连接超时 ms (毫秒) **/ + private Integer timeout = 10 * 1000; + + /** 过期时间,0=不过期 单位s **/ + private Integer expire = 1800; + private JedisPoolConfig jedisPoolConfig = null; + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getMasterName() { + return masterName; + } + + public void setMasterName(String masterName) { + this.masterName = masterName; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Integer getTimeout() { + return timeout; + } + + public void setTimeout(Integer timeout) { + this.timeout = timeout; + } + + public Integer getExpire() { + return expire; + } + + public void setExpire(Integer expire) { + this.expire = expire; + } + + public JedisPoolConfig getJedisPoolConfig() { + return jedisPoolConfig; + } + + public void setJedisPoolConfig(JedisPoolConfig jedisPoolConfig) { + this.jedisPoolConfig = jedisPoolConfig; + } + + static public Pool jedisPool = null; + + public void initJedisPoolConfig() { + jedisPoolConfig = new JedisPoolConfig(); + // 最大连接数 + jedisPoolConfig.setMaxTotal(1000); + // 最大空闲连接数 + jedisPoolConfig.setMaxIdle(5); // 设置最大空闲数 + jedisPoolConfig.setMinIdle(5); // 设置最小空闲数 + // 获取连接时的最大等待毫秒数 + jedisPoolConfig.setMaxWaitMillis(5000); + jedisPoolConfig.setTestOnBorrow(true); + } + + /** + * 初始化Jedis + * + * @throws Exception + */ + public synchronized void init() throws Exception { + String msg = ""; + if (StrUtils.isEmpty(address)) { + msg = "====redis 地址列表[address] 不能为空! "; + logger.error(msg); + throw new Exception(msg); + } + /* + * if(StrUtils.isEmpty(masterName)){ + * msg="====redis 主节点名称[masterName] 不能为空!"; logger.error(msg); throw new + * Exception(msg); } + */ + if (jedisPoolConfig == null) { + initJedisPoolConfig(); + logger.error(" JedisPoolConfig 没有配置请配置"); + } + if (StrUtils.isEmpty(password)) { + password = null; + } + HashSet sentinels = new HashSet(); + + String[] addresStrs = address.split(","); + for (String addresStr : addresStrs) { + if (StrUtils.isNotEmpty(address)) { + sentinels.add(addresStr); + } + } + logger.info("connect redis address={},masterName={}", address, masterName); + if (jedisPool != null) { + return; + } + if (StrUtils.isEmpty(masterName) && addresStrs.length == 1) { + String[] hostAndPort = addresStrs[0].split(":"); + String host = hostAndPort[0]; + int port = Integer.parseInt(hostAndPort[1]); + jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password); + } else { + jedisPool = new JedisSentinelPool(masterName, sentinels, jedisPoolConfig, timeout, password, + Protocol.DEFAULT_DATABASE); + } + } + + /** + * 根据byte数组key获取数据 + * + * @param key + * @return + */ + public byte[] get(byte[] key) { + Jedis jedis = null; + byte[] bytes = null; + try { + jedis = jedisPool.getResource(); + bytes = jedis.get(key); + } catch (Exception e) { + logger.error("获取缓存出错", e); + } finally { + jedis.close(); + } + return bytes; + } + + /** + * 根据String key获取数据 + * + * @param key + * @return + */ + public String get(String key) { + Jedis jedis = null; + String value = null; + try { + jedis = jedisPool.getResource(); + value = jedis.get(key); + } catch (Exception e) { + logger.error("获取缓存出错", e); + } finally { + jedis.close(); + } + return value; + } + + /** + * 跟进key ,value存储,value最大1G + * + * @param key + * @param value + */ + public void set(byte[] key, byte[] value) { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + String status = jedis.set(key, value); + if (this.expire != 0) { + jedis.expire(key, this.expire); + } + logger.info("jedis set status = {}", status); + } catch (Exception e) { + logger.error("保存缓存出错", e); + } finally { + jedis.close(); + } + } + + /** + * 跟进key ,value ,expire(单位s)过期时间存储,value最大1G + * + * @param key + * @param value + * @param expire + */ + public void set(byte[] key, byte[] value, int expire) { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + jedis.set(key, value); + if (expire != 0) { + jedis.expire(key, expire); + } + } catch (Exception e) { + logger.error("保存缓存出错", e); + } finally { + jedis.close(); + } + } + + public void set(String key, String value) { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + String status = jedis.set(key, value); + if (this.expire != 0) { + jedis.expire(key, this.expire); + } + logger.info("jedis set status = {}", status); + } catch (Exception e) { + logger.error("保存缓存出错", e); + } finally { + jedis.close(); + } + } + + /** + * 跟进key ,value ,expire(单位s)过期时间存储,value最大1G + * + * @param key + * @param value + * @param expire + */ + public void set(String key, String value, int expire) { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + String status = jedis.set(key, value); + if (expire != 0) { + jedis.expire(key, expire); + } + logger.info("jedis set status = {}", status); + } catch (Exception e) { + logger.error("保存缓存出错", e); + } finally { + jedis.close(); + } + } + + /** + * @param key + * @param value + * @return 返回true存储成功,否则失败 + */ + public boolean setnx(String key, String value) { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + // 返回1存储成功 返回0存储失败 + Long status = jedis.setnx(key, value); + if (this.expire != 0) { + jedis.expire(key, this.expire); + } + logger.info("jedis set status = {}", status); + return status == 1 ? true : false; + } catch (Exception e) { + logger.error("保存缓存出错", e); + return false; + } finally { + jedis.close(); + } + } + + /** + * 跟进key ,value ,expire(单位s)过期时间存储,value最大1G + * + * @param key + * @param value + * @param expire + * 返回true存储成功,否则失败 + */ + public boolean setnx(String key, String value, int expire) { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + Long status = jedis.setnx(key, value); + if (expire != 0) { + jedis.expire(key, expire); + } + return status == 1 ? true : false; + } catch (Exception e) { + logger.error("保存缓存出错", e); + return false; + } finally { + jedis.close(); + } + } + + /** + * 获取过期时间(单位s) + * + * @param key + */ + public Long ttl(String key) { + Jedis jedis = null; + Long expire = 0L; + try { + jedis = jedisPool.getResource(); + expire = jedis.ttl(key); + } catch (Exception e) { + logger.error("获取过期时间出错", e); + } finally { + jedis.close(); + } + return expire; + } + + /** + * 根据key删除缓存 + * + * @param key + */ + public void del(byte[] key) { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + Long l = jedis.del(key); + logger.info("jedis del number = {}", l); + } catch (Exception e) { + logger.error("删除缓存出错", e); + } finally { + jedis.close(); + } + } + + public void del(String key) { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + Long l = jedis.del(key); + logger.info("jedis del number = {}", l); + } catch (Exception e) { + logger.error("删除缓存出错", e); + } finally { + jedis.close(); + } + } + + /** + * 获取所有的keys + * + * @param pattern + * @return + */ + public Set keys(String pattern) { + Jedis jedis = null; + Set set = null; + try { + jedis = jedisPool.getResource(); + set = jedis.keys(pattern); + } catch (Exception e) { + logger.error("通过表达式获取keys出错", e); + } finally { + jedis.close(); + } + return set; + } + + public Set keys(byte[] pattern) { + Jedis jedis = null; + Set set = null; + try { + jedis = jedisPool.getResource(); + set = jedis.keys(pattern); + } catch (Exception e) { + logger.error("通过表达式获取keys出错", e); + } finally { + jedis.close(); + } + return set; + } + + /** + * 删除当前选中的DB + */ + public void delCurrentAll() { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + String status = jedis.flushDB(); + logger.info("jedis delAll status = {}", status); + } catch (Exception e) { + logger.error("删除DB出错", e); + } finally { + jedis.close(); + } + } + + /** + * 删除所有的DB + */ + public void delAll() { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + String status = jedis.flushAll(); + logger.info("jedis delAll status = {}", status); + } catch (Exception e) { + logger.error("删除所有DB出错", e); + } finally { + jedis.close(); + } + } + + /** + * 当前key 是否存在 + * + * @param key + * @return + */ + public boolean exists(String key) { + Jedis jedis = null; + Boolean exists = null; + + try { + jedis = jedisPool.getResource(); + exists = jedis.exists(key); + } catch (Exception e) { + logger.error("", e); + } finally { + if (null != jedis) { + jedis.close(); + } + } + + return exists; + } + + public synchronized String incrId(String key) { + Long value = null; + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + value = jedis.incr(key); + } catch (Exception e) { + logger.error("error in incrId method", e); + } finally { + if (null != jedis) { + jedis.close(); + } + } + return value + ""; + } + + /** + * @param key + * @param timeOut + * @param max + * @param incrNum + * @return + */ + public boolean maxLimit(String key, Integer timeOut, int max, int incrNum) { + boolean bo = false; + Jedis jedis = null; + Long currentNum = 0L; + try { + jedis = jedisPool.getResource(); + Long pttl = jedis.pttl(key); + if (pttl > 0) { + currentNum = jedis.incrBy(key, incrNum); + logger.info("****** Key = {" + key + "} ********* value = " + currentNum + " ******"); + if (currentNum <= max) { + bo = true; + } + } else if (pttl == -1 || pttl == -2 || pttl == 0) { + Transaction tx = jedis.multi(); + Response rsp = tx.incrBy(key, incrNum); + tx.expire(key, timeOut); + tx.exec(); + currentNum = rsp.get(); + if (currentNum <= max) { + bo = true; + } + } + } catch (Exception ex) { + logger.error("maxLimit :******Exception******", ex); + } finally { + if (null != jedis) { + jedis.close(); + } + } + return bo; + } + + /** + * @Title: allow @Description: 进行流量控制,允许访问返回true 不允许访问返回false + * @param: @param + * key 放入redis的key + * @param: @param + * timeOut 超时时间单位秒 + * @param: @param + * flowSize 超时时间内允许访问的次数 + * @param: @return + * @param: @throws + * Exception @return: boolean @throws + */ + public boolean flow(String key, Integer timeOut, Integer flowSize) { + boolean bo = false; + Jedis jedis = null; + Long flowNum = 0L; + try { + jedis = jedisPool.getResource(); + Long pttl = jedis.pttl(key); + if (pttl > 0) { + String flowNow = jedis.get(key); + if (StrUtils.isNotEmpty(flowNow)) { + flowNum = Long.parseLong(flowNow); + } + logger.info("==========[flow]===flow flowSize :" + flowSize); + logger.info("==========[flow]===flow flowNum :" + flowNum); + if (flowNum < flowSize) { + bo = true; + } + } else if (pttl == -1 || pttl == -2 || pttl == 0) { + Transaction tx = jedis.multi(); + Response rsp = tx.incr(key); + tx.expire(key, timeOut); + tx.exec(); + flowNum = rsp.get(); + if (flowNum < flowSize) { + bo = true; + } + } + } catch (Exception ex) { + logger.error("========Exception===", ex); + } finally { + if (null != jedis) { + jedis.close(); + } + } + return bo; + } + + public Long DBSize() { + return jedisPool.getResource().dbSize(); + } + + /** + * 获取所有key的值
+ * + * All the fields and values contained into a hash. + * + * @param key + * @return map + */ + public Map hgetAll(String key) { + Jedis jedis = null; + Map valueMap = new HashMap<>(); + try { + jedis = jedisPool.getResource(); + valueMap = jedis.hgetAll(key); + } catch (Exception e) { + logger.error("error in hgetAll method", e); + } finally { + if (null != jedis) { + jedis.close(); + } + } + return valueMap; + } + + /** + * 保存map
+ * Set the respective fields to the respective values. HMSET replaces old + * values with new values.
+ * + * If key does not exist, a new key holding a hash is created.
+ * + * @param key + * @param valueMap + * @return + */ + public String hmset(String key, Map valueMap) { + Jedis jedis = null; + String resp = null; + try { + jedis = jedisPool.getResource(); + resp = jedis.hmset(key, valueMap); + } catch (Exception e) { + logger.error("error in hmset method", e); + } finally { + if (null != jedis) { + jedis.close(); + } + } + return resp; + } + +} diff --git a/src/main/java/ga/clive/demo/shiro/cache/RedisSessionDAO.java b/src/main/java/ga/clive/demo/shiro/cache/RedisSessionDAO.java new file mode 100644 index 0000000000000000000000000000000000000000..8c8ebeb867463f135a14ac1b8813866b49c0898d --- /dev/null +++ b/src/main/java/ga/clive/demo/shiro/cache/RedisSessionDAO.java @@ -0,0 +1,142 @@ +package ga.clive.demo.shiro.cache; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.shiro.session.Session; +import org.apache.shiro.session.UnknownSessionException; +import org.apache.shiro.session.mgt.eis.AbstractSessionDAO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class RedisSessionDAO extends AbstractSessionDAO { + + private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class); + /** + * shiro-redis的session对象前缀 + */ + @Autowired + private RedisManager redisManager; + + /** + * The Redis key prefix for the sessions + */ + private String keyPrefix = "shiro_redis_session:"; + + @Override + public void update(Session session) throws UnknownSessionException { + this.saveSession(session); + } + + /** + * save session + * @param session + * @throws UnknownSessionException + */ + private void saveSession(Session session) throws UnknownSessionException{ + if(session == null || session.getId() == null){ + logger.error("session or session id is null"); + return; + } + + byte[] key = getByteKey(session.getId()); + byte[] value = SerializeUtils.serialize(session); + session.setTimeout(redisManager.getExpire()*1000); + this.redisManager.set(key, value, redisManager.getExpire()); + } + + @Override + public void delete(Session session) { + if(session == null || session.getId() == null){ + logger.error("session or session id is null"); + return; + } + redisManager.del(this.getByteKey(session.getId())); + + } + + @Override + @Deprecated + public Collection getActiveSessions() { + Set sessions = new HashSet(); + logger.error("getActiveSessions() Deprecated"); + //考虑到生产上redis数据量多, 不建议使用*查询 +// Set keys = redisManager.keys(this.keyPrefix + "*"); +// if(keys != null && keys.size()>0){ +// for(byte[] key:keys){ +// Session s = (Session)SerializeUtils.deserialize(redisManager.get(key)); +// sessions.add(s); +// } +// } + + return sessions; + } + + @Override + protected Serializable doCreate(Session session) { + Serializable sessionId = this.generateSessionId(session); + this.assignSessionId(session, sessionId); + this.saveSession(session); + return sessionId; + } + + @Override + protected Session doReadSession(Serializable sessionId) { + if(sessionId == null){ + logger.error("session id is null"); + return null; + } + + Session s = (Session)SerializeUtils.deserialize(redisManager.get(this.getByteKey(sessionId))); + return s; + } + + /** + * 获得byte[]型的key + * @param key + * @return + */ + private byte[] getByteKey(Serializable sessionId){ + String preKey = this.keyPrefix + sessionId; + return preKey.getBytes(); + } + + public RedisManager getRedisManager() { + return redisManager; + } + + public void setRedisManager(RedisManager redisManager) { + this.redisManager = redisManager; + + /** + * 初始化redisManager + */ + //无需初始化 + //this.redisManager.init(); + } + + /** + * Returns the Redis session keys + * prefix. + * @return The prefix + */ + public String getKeyPrefix() { + return keyPrefix; + } + + /** + * Sets the Redis sessions key + * prefix. + * @param keyPrefix The prefix + */ + public void setKeyPrefix(String keyPrefix) { + this.keyPrefix = keyPrefix; + } + + +} diff --git a/src/main/java/ga/clive/demo/shiro/cache/SerializeUtils.java b/src/main/java/ga/clive/demo/shiro/cache/SerializeUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..c307c555956748a15f1cb306ee39b8d367b2c545 --- /dev/null +++ b/src/main/java/ga/clive/demo/shiro/cache/SerializeUtils.java @@ -0,0 +1,85 @@ +package ga.clive.demo.shiro.cache; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SerializeUtils { + + private static Logger logger = LoggerFactory.getLogger(SerializeUtils.class); + + /** + * 反序列化 + * @param bytes + * @return + */ + public static Object deserialize(byte[] bytes) { + + Object result = null; + + if (isEmpty(bytes)) { + return null; + } + + try { + ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes); + try { + ObjectInputStream objectInputStream = new ObjectInputStream(byteStream); + try { + result = objectInputStream.readObject(); + } + catch (ClassNotFoundException ex) { + throw new Exception("Failed to deserialize object type", ex); + } + } + catch (Throwable ex) { + throw new Exception("Failed to deserialize", ex); + } + } catch (Exception e) { + logger.error("Failed to deserialize",e); + } + return result; + } + + public static boolean isEmpty(byte[] data) { + return (data == null || data.length == 0); + } + + /** + * 序列化 + * @param object + * @return + */ + public static byte[] serialize(Object object) { + + byte[] result = null; + + if (object == null) { + return new byte[0]; + } + try { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(128); + try { + if (!(object instanceof Serializable)) { + throw new IllegalArgumentException(SerializeUtils.class.getSimpleName() + " requires a Serializable payload " + + "but received an object of type [" + object.getClass().getName() + "]"); + } + ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream); + objectOutputStream.writeObject(object); + objectOutputStream.flush(); + result = byteStream.toByteArray(); + } + catch (Throwable ex) { + throw new Exception("Failed to serialize", ex); + } + } catch (Exception ex) { + logger.error("Failed to serialize",ex); + } + return result; + } +} diff --git a/src/main/resources/config/applicationContext.xml b/src/main/resources/config/applicationContext.xml index 34c80a12ebace73e21f8984c09070aaf46140350..12144d79feef3350badfc4e1391efafca114ae34 100644 --- a/src/main/resources/config/applicationContext.xml +++ b/src/main/resources/config/applicationContext.xml @@ -39,6 +39,7 @@ + diff --git a/src/main/resources/config/cache/spring-redis.xml b/src/main/resources/config/cache/spring-redis.xml new file mode 100644 index 0000000000000000000000000000000000000000..62ad03e46299c42b32297964ca0158976b08bc73 --- /dev/null +++ b/src/main/resources/config/cache/spring-redis.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/config/shiro-demo.properties b/src/main/resources/config/shiro-demo.properties index ea3d7760e7f2d50a5dad95bc6f423c4b911aa1b7..fab7e3a3ee128105b683dab82112c7bea109e8d1 100644 --- a/src/main/resources/config/shiro-demo.properties +++ b/src/main/resources/config/shiro-demo.properties @@ -1,10 +1,30 @@ + +######################## +## mysql config +######################## driver=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/shiro_demo?useUnicode=true&characterEncoding=utf8 username=root password=123456 -#c3p0 +######################## +## c3p0 config +######################## idleConnectionTestPeriod = 60 preferredTestQuery = select 1 testConnectionOnCheckin = true testConnectionOnCheckout = true + + +######################## +## redis config +######################## +redis.config.maxTotal=50 +redis.config.maxIdle=10 +redis.config.minIdle=10 +redis.config.maxWaitMillis=1800 +redis.address=redis:6379 +redis.masterName= +redis.password=namibankredis +redis.timeout=1800 +redis.expire=1800 \ No newline at end of file diff --git a/src/main/resources/config/shiro/spring-shiro.xml b/src/main/resources/config/shiro/spring-shiro.xml index dfafadbc1f80cd3e9033e6d807a0bbb6a89ff6a9..3e7af339965d66489d00c71a9e700778554cdffd 100644 --- a/src/main/resources/config/shiro/spring-shiro.xml +++ b/src/main/resources/config/shiro/spring-shiro.xml @@ -1,7 +1,8 @@ - - + @@ -32,26 +34,30 @@ - - - - - + + + + + + - - + + + + + + + - - - - - - - - + + + + +