upstream tomcat {
##ip_hash;
server 200.10.10.67:8110;
server 200.10.10.67:8120;
}
–注意jar版本号
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
<version>2.2version>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>2.8.1version>
dependency>
index=0 是redis 集群地址
index=1 是TGT 过期时间
index=2 是TGT 过期时间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<description>
Configuration for the default TicketRegistry which stores the tickets in-memory and cleans them out as specified intervals.
description>
<bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.RedisTicketRegistry">
<constructor-arg index="0" value="Centos6701:6379,Centos6701:6380,Centos6702:6380,Centos6702:6379,Centos6703:6380,Centos6703:6379" />
<constructor-arg index="1" value="28800" />
<constructor-arg index="2" value="200" />
bean>
beans>
RedisTicketRegistry.java
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.ticket.registry;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.validation.constraints.Min;
import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.Ticket;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.ticket.registry.util.RedisTools;
import org.jasig.cas.ticket.registry.util.serialize.ObjectsTranscoder;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
/**
* Key-value ticket registry implementation that stores tickets in redis keyed on the ticket ID.
*
* @author lumz
* @author 2016-7-12 10:17:30
* @since 1.0
*/
public final class RedisTicketRegistry extends AbstractDistributedTicketRegistry {
private JedisCluster jedisCluster;
private RedisTools handle;
private String redisServers;
/**
* TGT cache entry timeout in seconds.
*/
@Min(0)
private final int tgtTimeout;
/**
* ST cache entry timeout in seconds.
*/
@Min(0)
private final int stTimeout;
public RedisTicketRegistry(String redisServers,int tgtTimeout,int stTimeout){
logger.info("CoreRdisTicketRegistry {true} Init Redis Service : " + redisServers);
this.redisServers = redisServers;
this.tgtTimeout=tgtTimeout;
this.stTimeout=stTimeout;
Set jedisClusterNodes = new HashSet();
for (String server : redisServers.split(",")) {
jedisClusterNodes.add(new HostAndPort(server.split(":")[0], Integer
.parseInt(server.split(":")[1])));
}
jedisCluster = new JedisCluster(jedisClusterNodes);
handle = new RedisTools(jedisCluster);
logger.info("Init Redis Service sucess!");
logger.info("---------------------------CoreRdisTicketRegistry {true} ----------------------------=" + redisServers);
}
@Override
public void addTicket(Ticket ticket) {
logger.debug("Adding ticket {}", ticket);
try {
/*reidsTemplate.opsForValue().set(ticket.getId(),ticket, getTimeout(ticket), TimeUnit.SECONDS);*/
//序列化
ObjectsTranscoder objTranscoder = new ObjectsTranscoder();
byte[] result1 = objTranscoder.serialize(ticket);
// Ticket userA_userA = objTranscoder.deserialize(result1);
handle.set(ticket.getId().getBytes(), result1, getTimeout(ticket));
logger.info("---------------------------CoreRdisTicketRegistry { add ticket } -----------------------------" + ticket.getId());
} catch (final Exception e) {
logger.error("Failed adding {}", ticket, e);
}
}
@Override
public Ticket getTicket(String ticketId) {
Ticket tic =null;
try {
/*final Ticket t = (Ticket) this.reidsTemplate.opsForValue().get(ticketId);*/
String defaultvalue = "";
byte[] result = this.handle.get(ticketId.getBytes(), defaultvalue.getBytes());
if ("".equals(result)) {
return null;
}
//序列化
ObjectsTranscoder objTranscoder = new ObjectsTranscoder();
tic = objTranscoder.deserialize(result);
if (tic != null) {
logger.debug("Ticket [{}] found in registry.", ticketId);
/* return getProxiedTicketInstance(tic);*/
}
logger.info("---------------------------CoreRdisTicketRegistry { GET ticket } -----------------------------" + ticketId);
} catch (final Exception e) {
logger.error("Failed fetching {} ", ticketId, e);
}
return tic;
}
@Override
public boolean deleteTicket(String ticketId) {
logger.debug("Deleting ticket {}", ticketId);
try {
/*this.reidsTemplate.delete(ticketId);*/
this.handle.del(ticketId);
logger.info("---------------------------CoreRdisTicketRegistry { DELETE ticket } -----------------------------" + ticketId);
return true;
} catch (final Exception e) {
logger.error("Failed deleting {}", ticketId, e);
}
return false;
}
@Override
public Collection getTickets() {
throw new UnsupportedOperationException("GetTickets not supported.");
}
@Override
protected void updateTicket(Ticket ticket) {
logger.debug("Updating ticket {}", ticket);
try {
/*this.reidsTemplate.delete(ticket.getId());
reidsTemplate.opsForValue().set(ticket.getId(),ticket, getTimeout(ticket), TimeUnit.SECONDS);*/
this.handle.del(ticket.getId());
//序列化
ObjectsTranscoder objTranscoder = new ObjectsTranscoder();
byte[] result1 = objTranscoder.serialize(ticket);
handle.set(ticket.getId(), result1.toString(), getTimeout(ticket));
logger.info("---------------------------CoreRdisTicketRegistry { UPDATE ticket } -----------------------------" + ticket.getId());
} catch (final Exception e) {
logger.error("Failed updating {}", ticket, e);
}
}
@Override
protected boolean needsCallback() {
// TODO Auto-generated method stub
return true;
}
private int getTimeout(final Ticket t) {
if (t instanceof TicketGrantingTicket) {
return this.tgtTimeout;
} else if (t instanceof ServiceTicket) {
return this.stTimeout;
}
throw new IllegalArgumentException("Invalid ticket type");
}
}
序列化
/**
*
*/
package org.jasig.cas.ticket.registry.util.serialize;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import org.apache.log4j.Logger;
/**
* @author lumz
*
*/
public class ObjectsTranscoder<M extends Serializable> extends SerializeTranscoder {
protected static Logger logger = Logger.getLogger(SerializeTranscoder.class);
/* (non-Javadoc)
* @see org.jasig.cas.ticket.registry.util.serialize.SerializeTranscoder#serialize(java.lang.Object)
*/
@SuppressWarnings("unchecked")
@Override
public byte[] serialize(Object value) {
if (value == null) {
throw new NullPointerException("Can't serialize null");
}
byte[] result = null;
ByteArrayOutputStream bos = null;
ObjectOutputStream os = null;
try {
bos = new ByteArrayOutputStream();
os = new ObjectOutputStream(bos);
M m = (M) value;
os.writeObject(m);
os.close();
bos.close();
result = bos.toByteArray();
} catch (IOException e) {
throw new IllegalArgumentException("Non-serializable object", e);
} finally {
close(os);
close(bos);
}
return result;
}
/* (non-Javadoc)
* @see org.jasig.cas.ticket.registry.util.serialize.SerializeTranscoder#deserialize(byte[])
*/
@SuppressWarnings("unchecked")
@Override
public M deserialize(byte[] in) {
M result = null;
ByteArrayInputStream bis = null;
ObjectInputStream is = null;
try {
if (in != null) {
bis = new ByteArrayInputStream(in);
is = new ObjectInputStream(bis);
result = (M) is.readObject();
is.close();
bis.close();
}
} catch (IOException e) {
logger.error(String.format("Caught IOException decoding %d bytes of data",
in == null ? 0 : in.length) + e);
} catch (ClassNotFoundException e) {
logger.error(String.format("Caught CNFE decoding %d bytes of data",
in == null ? 0 : in.length) + e);
} finally {
close(is);
close(bis);
}
return result;
}
}
RedisTools.java
“`
/**
*
*/
package org.jasig.cas.ticket.registry.util;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
import redis.clients.jedis.Tuple;
/**
* @author lumz 2016-7-12 10:13:17
*
*/
public class RedisTools {
private JedisCluster jedisCluster;
public RedisTools(JedisCluster jedisCluster) {
super();
this.jedisCluster = jedisCluster;
}
// ----------------------- base handle -------------------------
/**
* 根据KEY删除记录
*
* @param key
* @return
*/
public boolean del(String key) {
jedisCluster.del(key);
return true;
}
/**
* 判断Key是否存在
*
* @param key
* @return
*/
public boolean exist(String key) {
return jedisCluster.exists(key);
}
/**
* 判断key+domain是否存在
*
* @param domain 域名
* @param key 键值
* @return
*/
public boolean existsHSet(String domain, String key) {
return jedisCluster.hexists(key, domain);
}
/**
* 全局扫描hset
*
* @param match field匹配模式
* @return
*/
public List> scanHSet(String key, String match) {
int cursor = 0;
ScanParams scanParams = new ScanParams();
scanParams.match(match);
ScanResult> scanResult;
List> list = new ArrayList>();
do {
scanResult = jedisCluster.hscan(key, String.valueOf(cursor));// .hscan(,
// scanParams);
list.addAll(scanResult.getResult());
cursor = Integer.parseInt(scanResult.getStringCursor());
} while (cursor > 0);
return list;
}
/**
* 返回 domain 指定的哈希集中所有字段的value值
*
* @param domain
* @return
*/
public List hvals(String domain) {
return jedisCluster.hvals(domain);
}
/**
* 返回 domain 指定的哈希集中所有字段的key值
*
* @param domain
* @return
*/
public Set hkeys(String domain) {
return jedisCluster.hkeys(domain);
}
/**
* 返回 domain 指定的哈希key值总数
*
* @param domain
* @return
*/
public long lenHset(String domain) {
return jedisCluster.hlen(domain);
}
/**
* 添加一个Key对应的Value,并设置有效期
*
* @param key
* @param value
* @param second
* @return
*/
public boolean set(String key, String value, int second) {
jedisCluster.setex(key, second, value);
return true;
}
/**
* 添加一个Key对应的Value,并设置有效期
*
* @param key
* @param value
* @param second
* @return
*/
public boolean set(byte[] key, byte[] value, int second) {
jedisCluster.setex(key, second, value);
return true;
}
/**
* 添加一个Key对应的Value
*
* @param key
* @param value
* @return
*/
public boolean set(String key, String value) {
jedisCluster.set(key, value);
return true;
}
/**
* 追加一个Key对应的Value
*
* @param key
* @param value
* @return
*/
public boolean append(String key, String value) {
jedisCluster.append(key, value);
return true;
}
/**
* 获得Key对应的Value
*
* @param key
* @param defaultValue
* @return
*/
public String get(String key, String defaultValue) {
String value = jedisCluster.get(key);
if (null != value && !"".equals(value)) {
return value;
} else {
return defaultValue;
}
}
/**
* 获得Key对应的Value
*
* @param key
* @param defaultValue
* @return
*/
public byte[] get(byte[] key, byte[] defaultValue) {
byte[] value = jedisCluster.get(key);
if (null != value && !"".equals(value)) {
return value;
} else {
return defaultValue;
}
}
/**
* 重命名key
*
* @param key
* @param newKey
* @return
*/
public boolean rename(String key, String newKey) {
jedisCluster.rename(key, newKey);
return true;
}
/**
* 数值+1
*
* @param key
* @return
*/
public long incr(String key) {
return jedisCluster.incr(key);
}
/**
* 数值-1
*
* @param key
* @return
*/
public long decr(String key) {
return jedisCluster.decr(key);
}
/**
* 设置过期时间
*
* @param key
* @param seconds
*/
public void expire(String key, int seconds) {
jedisCluster.expire(key, seconds);
}
// ----------------------- hSet handle -------------------------
/**
* 设置HashSet对象
*
* @param domain 域名
* @param key 键值
* @param value Json String or String value
* @return
*/
public boolean setHSet(String key, String domain, String value) {
jedisCluster.hset(key, domain, value);
return true;
}
/**
* 获得HashSet对象
*
* @param domain 域名
* @param key 键值
* @return Json String or String value
*/
public String getHSet(String domain, String key) {
return jedisCluster.hget(key, domain);
}
/**
* 获取存储在键的散列的所有字段和值
*
* @param key 键值
* @return
*/
public Map getHGetAll(String key) {
return jedisCluster.hgetAll(key);
}
/**
* 删除HashSet对象
*
* @param domain 域名
* @param key 键值
* @return 删除的记录数
*/
public long delHSet(String domain, String key) {
return jedisCluster.hdel(key, domain);
}
/**
* 删除HashSet对象的指定domains
*
* @param domain 域名
* @param key 键值
* @return 删除的记录数
*/
public long delHSet(String key, String... domain) {
return jedisCluster.hdel(key, domain);
}
// ----------------------- sortedSet handle -------------------------
/**
* 设置排序集合
*
* @param key
* @param score
* @param value
* @return
*/
public boolean setSortedSet(String key, long score, String value) {
jedisCluster.zadd(key, score, value);
return true;
}
/**
* 获得排序集合
*
* @param key
* @param startScore
* @param endScore
* @param orderByDesc
* @return
*/
public Set getSoredSet(String key, long startScore, long endScore, boolean orderByDesc) {
if (orderByDesc) {
return jedisCluster.zrevrangeByScore(key, endScore, startScore);
} else {
return jedisCluster.zrangeByScore(key, startScore, endScore);
}
}
/**
* 获得排序集合
*
* @param key
* @param startScore
* @param endScore
* @param count
* @param offset
* @param orderByDesc
* @return
*/
public Set getSoredSet(String key, long startScore, long endScore, int offset, int count,
boolean orderByDesc) {
if (orderByDesc) {
return jedisCluster.zrevrangeByScore(key, endScore, startScore, offset, count);
} else {
return jedisCluster.zrangeByScore(key, startScore, endScore, offset, count);
}
}
/**
* 计算排序长度
*
* @param key
* @param startScore
* @param endScore
* @return
*/
public long countSoredSet(String key, long startScore, long endScore) {
Long count = jedisCluster.zcount(key, startScore, endScore);
return count == null ? 0L : count;
}
/**
* 计算排序长度
*
* @param key
* @return
*/
public long countSoredSet(String key) {
Long count = jedisCluster.zcard(key);
return count == null ? 0L : count;
}
/**
* 删除排序集合
*
* @param key
* @param value
* @return
*/
public boolean delSortedSet(String key, String value) {
long count = jedisCluster.zrem(key, value);
return count > 0;
}
/**
* 删除排序集合
*
* @param key
* @param start
* @param end
* @return
*/
public boolean delRankSortedSet(String key, long start, long end) {
long count = jedisCluster.zremrangeByRank(key, start, end);
return count > 0;
}
/**
* 删除排序部分集合
*
* @param key
* @return
*/
public boolean delSortedSet(String key, long minScore, long maxScore) {
long count = jedisCluster.zremrangeByScore(key, minScore, maxScore);
return count > 0;
}
/**
* 根据分数排序获得前N个
*
* @param key
* @param topN
* @return
*/
public Set zrevrangeWithScores(String key, int topN) {
return jedisCluster.zrevrangeWithScores(key, 0, topN);
}
/**
* 根据分数排序从指定位置获得数据
*
* @param key
* @param start
* @param end
* @return
*/
public Set zrevrangeWithScores(String key, long start, long end) {
return jedisCluster.zrevrangeWithScores(key, start, end);
}
// ----------------------- List handle -------------------------
/**
* 列表lpush操作
* 命令将一个或多个值插入到列表头部
*
* @param key
* @param value
*/
public void lpush(String key, String... value) {
jedisCluster.lpush(key, value);
}
/**
* 列表rpush操作
* 将一个或多个值插入到列表的尾部(最右边)
*
* @param key
* @param value
*/
public void rpush(String key, String... value) {
jedisCluster.rpush(key, value);
}
/**
* 列表长度
*
* @param key
*/
public long llen(String key) {
return jedisCluster.llen(key);
}
/**
* 根据start和stop获取列表
*
* @param key
* @param start
* @param stop
* @return
*/
public List lrange(String key, int start, int stop) {
return jedisCluster.lrange(key, start, stop);
}
/**
* 列表删除
*
* @param key
* @param count count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。 count < 0 :
* 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。 count = 0 : 移除表中所有与
* value 相等的值。
* @param value
* @return
*/
public long lrem(String key, int count, String value) {
return jedisCluster.lrem(key, count, value);
}
/**
* 列表清理
*
* @param key
* @param start
* @param end
*/
public void ltrim(String key, int start, int end) {
jedisCluster.ltrim(key, start, end);
}
/**
* 将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略
*
* @param key
* @param member
* @return
*/
public Long sadd(String key, String... member) {
return jedisCluster.sadd(key, member);
}
/**
* 删除成员
*
* @param key
* @param member
* @return
*/
public Long srem(String key, String... member) {
return jedisCluster.srem(key, member);
}
/**
* 获取set所有成员
*
* @param key
* @return
*/
public Set smembers(String key) {
return jedisCluster.smembers(key);
}
/**
* 判断member元素是否是集合key的成员
*
* @param key
* @param member
* @return
*/
public boolean sismember(String key, String member) {
return jedisCluster.sismember(key, member);
}
// ----------------------- mSet handle -------------------------
public String setMSet(String key, Map hash) {
return jedisCluster.hmset(key, hash);
}
}
“`SerializeTranscoder
package org.jasig.cas.ticket.registry.util.serialize;
import java.io.Closeable;
import org.apache.log4j.Logger;
/**
* @author lumz
*
* @date 2016-7-12 10:44:07
*/
public abstract class SerializeTranscoder {
protected static Logger logger = Logger.getLogger(SerializeTranscoder.class);
public abstract byte[] serialize(Object value);
public abstract Object deserialize(byte[] in);
public void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception e) {
logger.info("Unable to close " + closeable, e);
}
}
}
}
【1】Tomcat redis session manager
【2】实现CAS Ticket 基于redis的集群