• 基于Redis的CAS集群


    单点登录(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,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    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值

    1
    2
    3
    4
    5
        <!-- Ticket Registry -->
        <bean id="ticketRegistry" class="org.jasig.cas.util.RedisTicketRegistry" />
         
    <!--     <bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.DefaultTicketRegistry" />
     -->

     

    因为使用了Redis的expire功能,注释掉如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!-- TICKET REGISTRY CLEANER --> 
    lt;!--  <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,实现负载均衡和容错目的。

  • 相关阅读:
    二级菜单jquery
    js二级导航
    append appendChild appendTo区别
    cssText在 IE6/7/8和chrome/Firefox/IE9+的不同
    cssText
    css slice和splice
    js字符串函数之substring() substr()
    js字符串函数之split()join()
    Python面向对象之:三大特性:继承,封装,多态以及类的约束
    Python3 面向对象之:多继承
  • 原文地址:https://www.cnblogs.com/lvfeilong/p/34dsfdsf.html
Copyright © 2020-2023  润新知