注:转载自 http://blog.csdn.net/roadmap001/article/details/8686301
单点登录(SSO)是复杂应用系统的基本需求,Yale CAS是目前常用的开源解决方案。CAS认证中心,基于其特殊作用,自然会成为整个应用系统的核心,所有应用系统的认证工作,都将请求到CAS来完成。因此CAS服务器是整个应用的关键节点,CAS发生故障,所有系统都将陷入瘫痪。同时,CAS的负载能力要足够强,能够承担所有的认证请求响应。利用负载均衡和集群技术,不仅能克服CAS单点故障,同时将认证请求分布到多台CAS服务器上,有效减轻单台CAS服务器的请求压力。下面将基于CAS 3.4.5来讨论下CAS集群。
CAS的工作原理,主要是基于票据(Ticket)来实现的(参见 CAS基本原理)。CAS票据,存储在TicketRegistry中,因此要想实现CAS Cluster, 必须要多台CAS之间共享所有的Ticket,采用统一的TicketRegistry,可以达到此目的。 缺省的CAS实现中,TicketRegistry在内存中实现,不同的CAS服务器有自己单独的TicketRegistry,因此是不支持分布式集群的。但CAS提供了支持TicketRegistry分布式的接口org.jasig.cas.ticket.registry.AbstractDistributedTicketRegistry,我们可以实现这个接口实现多台CAS服务器TicketRegistry共享,从而实现CAS集群。
同时,较新版本CAS使用SpringWebFlow作为认证流程,而webflow需要使用session存储流程相关信息,因此实现CAS集群,我们还得需要让不同服务器的session进行共享。
我们采用内存数据库Redis来实现TicketRegistry,让多个CAS服务器共用同一个TicketRegistry。同样方法,我们让session也存储在Redis中,达到共享session的目的。下面就说说如何用Redis来实现TicketRegistry,我们使用Java调用接口Jedis来操作Redis,代码如下:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Collection;
import org.jasig.cas.ticket.Ticket;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.ticket.registry.AbstractDistributedTicketRegistry;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/*
* TicketRegistry using Redis, to solve CAS Cluster.
*
* @author ZL
*
*/
public class RedisTicketRegistry extends AbstractDistributedTicketRegistry {
private static int redisDatabaseNum;
private static String hosts;
private static int port;
private static int st_time; //ST最大空闲时间
private static int tgt_time; //TGT最大空闲时间
private static JedisPool cachePool;
static {
redisDatabaseNum = PropertiesConfigUtil.getPropertyInt("redis_database_num");
hosts = PropertiesConfigUtil.getProperty("hosts");
port = PropertiesConfigUtil.getPropertyInt("port");
st_time = PropertiesConfigUtil.getPropertyInt("st_time");
tgt_time = PropertiesConfigUtil.getPropertyInt("tgt_time");
cachePool = new JedisPool(new JedisPoolConfig(), hosts, port);
}
public void addTicket(Ticket ticket) {
Jedis jedis = cachePool.getResource();
jedis.select(redisDatabaseNum);
int seconds = 0;
String key = ticket.getId() ;
if(ticket instanceof TicketGrantingTicket){
//key = ((TicketGrantingTicket)ticket).getAuthentication().getPrincipal().getId();
seconds = tgt_time/1000;
}else{
seconds = st_time/1000;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
try{
oos = new ObjectOutputStream(bos);
oos.writeObject(ticket);
}catch(Exception e){
log.error("adding ticket to redis error.");
}finally{
try{
if(null!=oos) oos.close();
}catch(Exception e){
log.error("oos closing error when adding ticket to redis.");
}
}
jedis.set(key.getBytes(), bos.toByteArray());
jedis.expire(key.getBytes(), seconds);
cachePool.returnResource(jedis);
}
public Ticket getTicket(final String ticketId) {
return getProxiedTicketInstance(getRawTicket(ticketId));
}
private Ticket getRawTicket(final String ticketId) {
if(null == ticketId) return null;
Jedis jedis = cachePool.getResource();
jedis.select(redisDatabaseNum);
Ticket ticket = null;
ByteArrayInputStream bais = new ByteArrayInputStream(jedis.get(ticketId.getBytes()));
ObjectInputStream ois = null;
try{
ois = new ObjectInputStream(bais);
ticket = (Ticket)ois.readObject();
}catch(Exception e){
log.error("getting ticket to redis error.");
}finally{
try{
if(null!=ois) ois.close();
}catch(Exception e){
log.error("ois closing error when getting ticket to redis.");
}
}
cachePool.returnResource(jedis);
return ticket;
}
public boolean deleteTicket(final String ticketId) {
if (ticketId == null) {
return false;
}
Jedis jedis = cachePool.getResource();
jedis.select(redisDatabaseNum);
jedis.del(ticketId.getBytes());
cachePool.returnResource(jedis);
return true;
}
public Collection<Ticket> getTickets() {
throw new UnsupportedOperationException("GetTickets not supported.");
}
protected boolean needsCallback() {
return false;
}
protected void updateTicket(final Ticket ticket) {
addTicket(ticket);
}
}
同时,我们在ticketRegistry.xml配置文件中,将TicketRegistry实现类指定为上述实现。即修改下面的class值
<!-- Ticket Registry -->
<bean id="ticketRegistry" class="org.jasig.cas.util.RedisTicketRegistry" />
<!-- <bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.DefaultTicketRegistry" />
-->
因为使用了Redis的expire功能,注释掉如下代码:
<!-- TICKET REGISTRY CLEANER -->
<!-- <bean id="ticketRegistryCleaner" class="org.jasig.cas.ticket.registry.support.DefaultTicketRegistryCleaner"
p:ticketRegistry-ref="ticketRegistry" />
<bean id="jobDetailTicketRegistryCleaner" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
p:targetObject-ref="ticketRegistryCleaner"
p:targetMethod="clean" />
<bean id="triggerJobDetailTicketRegistryCleaner" class="org.springframework.scheduling.quartz.SimpleTriggerBean"
p:jobDetail-ref="jobDetailTicketRegistryCleaner"
p:startDelay="20000"
p:repeatInterval="5000000" /> -->
通过上述实现TicketRegistry,多台CAS服务器就可以共用同一个TicketRegistry。对于如何共享session,我们可以采用现成的第三方工具tomcat-redis-session-manager直接集成即可。对于前端web服务器(如nginx),做好负载均衡配置,将认证请求分布转发给后面多台CAS,实现负载均衡和容错目的。
分享到:
相关推荐
cas4.2.7服务端+cas客户端+示例程序+环境搭建之客户端war包 一切跑不起来的程序和走不通的教程都是耍流氓,二话不说,先按照我的步骤把程序跑起来在说吧。 请看博客...
10.CAS-redisCluster集群存储ticket(相应redis必须配置成cluster集群) 11.CAS-加密存储ticket 12.CAS-实习动态验证码 13.CAS-实习自定义登录 14.CAS-实现自定义返回用户登录信息 15.CAS-页面缓存记住我 ----------...
Tcp服务端与客户端的JAVA实例源代码 2个目标文件 摘要:Java源码,文件操作,TCP,服务器 Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多...
Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...
单点登录:cas 权限管理:SpringSecurity, 跨域:cros 支付:微信扫描 短信验证:阿里大于 密码加密:BCrypt 富文本:KindEditor 事务:声明式事务 任务调度:spring task 所有的技术,都可能涉及到为什么用?怎么...
day01_电商介绍--互联网术语-SOA-分布式-集群介绍-环境配置-框架搭建 day02_Dubbo介绍_dubbo框架整合_商品列表查询实现_分页_逆向工程 day03_Git day04_门户网站介绍&商城首页搭建&内容系统创建&CMS实现 day05_...
集群 分片 Key-hash 异步 一致性hash 消峰 分库分表 锁 悲观锁 乐观锁 行级锁 分布式锁 分区排队 一致性 一致性算法 paxos zab nwr raft gossip 柔性事务(TCC) 一致性原理 CAP BASE ...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...
支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL...