shiro和redis集成的shiro-redis是单个的redis,根据项目需要,把shiro-redis单个的redis改为集群模式,
首先自己新建一个项目:或者打开shiro-redis项目的源码:
原作者下载链接:https://github.com/alexxiyang/shiro-redis.git 原作者现在修改了许多东西,
此处是自己当时派生过来的仓库地址:https://github.com/zhshyong/shiro-redis.git
下面的改造全是根据自己派生过来的仓库地址代码改造而成
pom.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.crazycake</groupId> <artifactId>org.crazycake</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.2.5.RELEASE</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.2</version> </dependency> </dependencies> <build> <finalName>shiro.redis.cluster</finalName> <sourceDirectory>src/main/java</sourceDirectory> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <excludes> <exclude>*.xml</exclude> </excludes> </configuration> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.7.6.201602180812</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <phase>prepare-package</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
首先改造org.crazycake.shiro.RedisManager 类,原来单个redis里面是写死的:
/** * Copyright 2017 Inc. **/ package org.crazycake.shiro; import redis.clients.jedis.JedisCluster; import java.util.TreeSet; /** * @author zhangshaoyong */ public class RedisManager { private JedisCluster jedisCluster; private RedisOperator redisOperator; private int expire = 0; public RedisOperator getRedisOperator() { return redisOperator; } public void setRedisOperator(RedisOperator redisOperator) { this.redisOperator = redisOperator; } public int getExpire() { return expire; } public void setExpire(int expire) { this.expire = expire; } public JedisCluster getJedisCluster() { return jedisCluster; } public void setJedisCluster(JedisCluster jedisCluster) { this.jedisCluster = jedisCluster; } public RedisManager() { } /** * 初始化方法 */ public void init() { } /** * get value from redis * * @param key * @return */ public byte[] get(byte[] key) { byte[] value = null; value = jedisCluster.get(key); return value; } /** * set * * @param key * @param value * @return */ public byte[] set(byte[] key, byte[] value) { jedisCluster.set(key, value); if (this.expire != 0) { jedisCluster.expire(key, this.expire); } return value; } /** * set * * @param key * @param value * @param expire * @return */ public byte[] set(byte[] key, byte[] value, int expire) { jedisCluster.set(key, value); if (expire != 0) { jedisCluster.expire(key, expire); } return value; } /** * del * * @param key */ public void del(byte[] key) { jedisCluster.del(key); } /** * flush */ public void flushDB() throws Exception { redisOperator.flushDB(); } /** * size */ public Long dbSize(String pattern) { Long dbSize = 0L; TreeSet<String> keys = null; try { keys = keys(pattern); } catch (Exception e) { e.printStackTrace(); } dbSize = Long.valueOf(keys.size()); return dbSize; } /** * keys * * @param pattern * @return */ public TreeSet<String> keys(String pattern) throws Exception { TreeSet<String> treeSet = new TreeSet<String>(); treeSet = redisOperator.keys(pattern); return treeSet; } }
把单个的改为redis集群,使用了集群:所以其中的keys(String pattern)方法以及flushDB()方法都需要自己去写:故引入了
RedisOperator类:其中就是写了flushDB以及keys(String pattern)方法:内容如下:
/** * Copyright 2017 Inc. **/ package org.crazycake.shiro; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPool; import java.util.*; /** * @author zhangshaoyong */ public class RedisOperator implements IRedisOperator { public final static Logger logger = LoggerFactory.getLogger(RedisOperator.class); private JedisCluster jedisCluster; @Override public TreeSet<String> keys(String pattern) throws Exception { logger.debug("Start getting keys..."); TreeSet<String> keys = new TreeSet<String>(); Map<String, JedisPool> clusterNodes = jedisCluster.getClusterNodes(); for(String k : clusterNodes.keySet()){ logger.debug("Getting keys from: {}", k); JedisPool jp = clusterNodes.get(k); Jedis connection = jp.getResource(); try { Set<String> set = connection.keys(pattern); Iterator it = set.iterator(); while (it.hasNext()) { keys.add((String) it.next()); } } catch(Exception e){ logger.error("Getting keys error: {}", e); } finally{ logger.debug("Connection closed."); connection.close();//用完关闭连接 } } logger.debug("Keys gotten!"); return keys; } @Override public void flushDB() throws Exception { logger.debug("Start flushDb keys..."); Map<String, JedisPool> clusterNodes = jedisCluster.getClusterNodes(); for(String k : clusterNodes.keySet()){ logger.debug("delete keys from: {}", k); JedisPool jp = clusterNodes.get(k); Jedis connection = jp.getResource(); try { connection.flushDB(); } catch(Exception e){ logger.error("Getting keys error: {}", e); } finally{ logger.debug("Connection closed."); connection.close(); } } logger.debug("flushDB"); } public JedisCluster getJedisCluster() { return jedisCluster; } public void setJedisCluster(JedisCluster jedisCluster) { this.jedisCluster = jedisCluster; } }
IRedisOperator接口:
/** * Copyright 2017 Inc. **/ package org.crazycake.shiro; import java.util.TreeSet; /** * @author zhangshaoyong */ public interface IRedisOperator { /** * 根据pattern 获取所有的keys * @param pattern * @return */ TreeSet<String> keys(String pattern) throws Exception; /** * 删除所有的keys */ void flushDB()throws Exception; }
最后写一个JedisClusterFactory工厂来创建redis集群即可
内容如下:
/** * Copyright 2017 Inc. **/ package org.crazycake.shiro; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.io.Resource; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster; import java.util.HashSet; import java.util.Iterator; import java.util.Properties; import java.util.Set; import java.util.regex.Pattern; /** * @author zhangshaoyong */ public class JedisClusterFactory implements FactoryBean<JedisCluster>, InitializingBean { private Resource addressConfig; private String addressKeyPrefix; private JedisCluster jedisCluster; private Integer timeout; private Integer maxRedirections; private GenericObjectPoolConfig genericObjectPoolConfig; private Pattern p = Pattern.compile("^.+[:]\\d{1,5}\\s*$"); public JedisClusterFactory() { } public JedisCluster getObject() throws Exception { return this.jedisCluster; } public Class<? extends JedisCluster> getObjectType() { return this.jedisCluster != null?this.jedisCluster.getClass():JedisCluster.class; } public boolean isSingleton() { return true; } private Set<HostAndPort> parseHostAndPort() throws Exception { try { Properties ex = new Properties(); ex.load(this.addressConfig.getInputStream()); HashSet haps = new HashSet(); Iterator i$ = ex.keySet().iterator(); while(i$.hasNext()) { Object key = i$.next(); if(((String)key).startsWith(this.addressKeyPrefix)) { String val = (String)ex.get(key); boolean isIpPort = this.p.matcher(val).matches(); if(!isIpPort) { throw new IllegalArgumentException("ip 或 port 不合法"); } String[] ipAndPort = val.split(":"); HostAndPort hap = new HostAndPort(ipAndPort[0], Integer.parseInt(ipAndPort[1])); haps.add(hap); } } return haps; } catch (IllegalArgumentException var9) { throw var9; } catch (Exception var10) { throw new Exception("解析 jedis 配置文件失败", var10); } } public void afterPropertiesSet() throws Exception { Set haps = this.parseHostAndPort(); this.jedisCluster = new JedisCluster(haps, this.timeout.intValue(), this.maxRedirections.intValue(), this.genericObjectPoolConfig); } public void setAddressConfig(Resource addressConfig) { this.addressConfig = addressConfig; } public void setTimeout(int timeout) { this.timeout = Integer.valueOf(timeout); } public void setMaxRedirections(int maxRedirections) { this.maxRedirections = Integer.valueOf(maxRedirections); } public void setAddressKeyPrefix(String addressKeyPrefix) { this.addressKeyPrefix = addressKeyPrefix; } public void setGenericObjectPoolConfig(GenericObjectPoolConfig genericObjectPoolConfig) { this.genericObjectPoolConfig = genericObjectPoolConfig; } }
使用配置如下:
<?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:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- jedis cluster config --> <bean name="genericObjectPoolConfig" class="org.apache.commons.pool2.impl.GenericObjectPoolConfig" > <property name="maxWaitMillis" value="-1" /> <property name="maxTotal" value="1000" /> <property name="minIdle" value="8" /> <property name="maxIdle" value="100" /> </bean> <bean id="jedisCluster" class="org.crazycake.shiro.JedisClusterFactory"> <property name="addressConfig"> <value>classpath:/redis-config.properties</value> </property> <property name="addressKeyPrefix" value="address" /> <property name="timeout" value="300000"/> <property name="maxRedirections" value="6" /> <property name="genericObjectPoolConfig" ref="genericObjectPoolConfig" /> </bean> <bean id="redisManager" class="org.crazycake.shiro.RedisManager"> <property name="jedisCluster" ref="jedisCluster"/> <property name="redisOperator" ref="redisOperator"/> <property name="expire" value="1800"/> </bean> <bean id="redisOperator" class="org.crazycake.shiro.RedisOperator"> <property name="jedisCluster" ref="jedisCluster"/> </bean> <!-- cacheManager --> <bean id="redisCacheManager" class="org.crazycake.shiro.RedisCacheManager"> <property name="redisManager" ref="redisManager" /> </bean> <!-- redisCache --> <bean id="redisCache" class="org.crazycake.shiro.RedisCache"> <constructor-arg ref="redisManager"></constructor-arg> </bean> </beans>
其中redis-config.properties内容如下:必须遵从此格式(或者自己也能改为自己解析的格式)
address1=127.0.0.1:7000 address2=127.0.0.1:7001 address3=127.0.0.1:7002 address4=127.0.0.1:7003 address5=127.0.0.1:7004 address6=127.0.0.1:7005
和shiro整合时候的配置
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="realm"/>
<property name="cacheManager" ref="shiroRedisCacheManager"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<bean id="shiroRedisCacheManager" class="org.crazycake.shiro.RedisCacheManager">
<property name="redisManager" ref="redisManager"/>
</bean>
<bean id="redisManager" class="org.crazycake.shiro.RedisManager">
<property name="jedisCluster" ref="jedisCluster"/>
<property name="redisOperator" ref="redisOperator"/>
<property name="expire" value="180000"/>
</bean>
<bean id="jedisCluster" class="org.crazycake.shiro.JedisClusterFactory">
<property name="addressConfig" value="classpath:/shiro-redis.properties"/>
<property name="addressKeyPrefix" value="address"/>
<property name="timeout" value="300000"/>
<property name="maxRedirections" value="6"/>
<property name="genericObjectPoolConfig" ref="genericObjectPoolConfig"/>
</bean>
<bean id="redisOperator" class="org.crazycake.shiro.RedisOperator">
<property name="jedisCluster" ref="jedisCluster"/>
</bean>
项目完整地址:https://github.com/zhshyong/shiro-redis-cluster.git
如有不对之处:请大佬们指出,