基於Redis的CAS叢集

來源:互聯網
上載者:User

單點登入(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,代碼如下:

 

[html]
view plaincopy
  1. import java.io.ByteArrayInputStream;  
  2. import java.io.ByteArrayOutputStream;  
  3. import java.io.ObjectInputStream;  
  4. import java.io.ObjectOutputStream;  
  5. import java.util.Collection;  
  6.   
  7. import org.jasig.cas.ticket.Ticket;  
  8. import org.jasig.cas.ticket.TicketGrantingTicket;  
  9. import org.jasig.cas.ticket.registry.AbstractDistributedTicketRegistry;  
  10.   
  11.   
  12. import redis.clients.jedis.Jedis;  
  13. import redis.clients.jedis.JedisPool;  
  14. import redis.clients.jedis.JedisPoolConfig;  
  15.   
  16.   
  17. /*  
  18.  *  TicketRegistry using Redis, to solve CAS Cluster.  
  19.  *    
  20.  *  @author ZL  
  21.  *   
  22.  */  
  23.   
  24. public class RedisTicketRegistry extends AbstractDistributedTicketRegistry {  
  25.   
  26.       
  27.     private static int redisDatabaseNum;  
  28.     private static String hosts;  
  29.     private static int port;  
  30.          private static int st_time;  //ST最大空閑時間  
  31.           private static int tgt_time; //TGT最大空閑時間  
  32.       
  33.     private static JedisPool cachePool;  
  34.       
  35.     static {  
  36.       
  37.         redisDatabaseNum = PropertiesConfigUtil.getPropertyInt("redis_database_num");  
  38.         hosts = PropertiesConfigUtil.getProperty("hosts");  
  39.         port = PropertiesConfigUtil.getPropertyInt("port");  
  40.         st_time = PropertiesConfigUtil.getPropertyInt("st_time");  
  41.         tgt_time = PropertiesConfigUtil.getPropertyInt("tgt_time");  
  42.         cachePool = new JedisPool(new JedisPoolConfig(), hosts, port);  
  43.           
  44.     }  
  45.       
  46.     public void addTicket(Ticket ticket) {  
  47.               
  48.         Jedis jedis = cachePool.getResource();  
  49.         jedis.select(redisDatabaseNum);  
  50.           
  51.                   int seconds = 0;  
  52.   
  53.                   String key = ticket.getId() ;  
  54.           
  55.         if(ticket instanceof TicketGrantingTicket){  
  56.             //key = ((TicketGrantingTicket)ticket).getAuthentication().getPrincipal().getId();  
  57.             seconds = tgt_time/1000;  
  58.         }else{  
  59.             seconds = st_time/1000;  
  60.         }  
  61.     
  62.           
  63.         ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  64.         ObjectOutputStream oos = null;  
  65.         try{  
  66.             oos = new ObjectOutputStream(bos);  
  67.             oos.writeObject(ticket);  
  68.              
  69.         }catch(Exception e){  
  70.             log.error("adding ticket to redis error.");  
  71.         }finally{  
  72.             try{   
  73.                 if(null!=oos) oos.close();  
  74.             }catch(Exception e){  
  75.                 log.error("oos closing error when adding ticket to redis.");  
  76.             }  
  77.         }  
  78.         jedis.set(key.getBytes(), bos.toByteArray());  
  79.         jedis.expire(key.getBytes(), seconds);  
  80.           
  81.         cachePool.returnResource(jedis);  
  82.           
  83.     }  
  84.       
  85.     public Ticket getTicket(final String ticketId) {  
  86.         return getProxiedTicketInstance(getRawTicket(ticketId));  
  87.     }  
  88.       
  89.       
  90.     private Ticket getRawTicket(final String ticketId) {  
  91.           
  92.         if(null == ticketId) return null;  
  93.           
  94.         Jedis jedis = cachePool.getResource();  
  95.         jedis.select(redisDatabaseNum);  
  96.           
  97.         Ticket ticket = null;  
  98.           
  99.         ByteArrayInputStream bais = new ByteArrayInputStream(jedis.get(ticketId.getBytes()));  
  100.         ObjectInputStream ois = null;  
  101.           
  102.         try{  
  103.             ois = new ObjectInputStream(bais);  
  104.             ticket = (Ticket)ois.readObject();   
  105.         }catch(Exception e){  
  106.             log.error("getting ticket to redis error.");  
  107.         }finally{  
  108.             try{  
  109.                 if(null!=ois)  ois.close();  
  110.             }catch(Exception e){  
  111.                 log.error("ois closing error when getting ticket to redis.");  
  112.             }  
  113.         }  
  114.           
  115.         cachePool.returnResource(jedis);  
  116.           
  117.         return ticket;  
  118.     }  
  119.      
  120.       
  121.   
  122.     public boolean deleteTicket(final String ticketId) {  
  123.           
  124.         if (ticketId == null) {  
  125.             return false;  
  126.         }  
  127.           
  128.           
  129.         Jedis jedis = cachePool.getResource();  
  130.         jedis.select(redisDatabaseNum);  
  131.               
  132.         jedis.del(ticketId.getBytes());  
  133.           
  134.         cachePool.returnResource(jedis);  
  135.           
  136.         return true;          
  137.     }  
  138.    
  139.     public Collection<Ticket> getTickets() {  
  140.           
  141.         throw new UnsupportedOperationException("GetTickets not supported.");  
  142.   
  143.     }  
  144.   
  145.     protected boolean needsCallback() {  
  146.         return false;  
  147.     }  
  148.       
  149.     protected void updateTicket(final Ticket ticket) {  
  150.         addTicket(ticket);  
  151.     }  
  152.    
  153. }  

同時,我們在ticketRegistry.xml設定檔中,將TicketRegistry實作類別指定為上述實現。即修改下面的class值

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

因為使用了Redis的expire功能,注釋掉如下代碼:

[html]
view plaincopy
  1. <!-- TICKET REGISTRY CLEANER -->  
  2. lt;!--  <bean id="ticketRegistryCleaner" class="org.jasig.cas.ticket.registry.support.DefaultTicketRegistryCleaner"  
  3.     p:ticketRegistry-ref="ticketRegistry" />  
  4.   
  5. <bean id="jobDetailTicketRegistryCleaner" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"  
  6.     p:targetObject-ref="ticketRegistryCleaner"  
  7.     p:targetMethod="clean" />  
  8.   
  9. <bean id="triggerJobDetailTicketRegistryCleaner" class="org.springframework.scheduling.quartz.SimpleTriggerBean"  
  10.     p:jobDetail-ref="jobDetailTicketRegistryCleaner"  
  11.     p:startDelay="20000"  
  12.     p:repeatInterval="5000000" /> -->  

通過上述實現TicketRegistry,多台CAS伺服器就可以共用同一個TicketRegistry。對於如何共用session,我們可以採用現成的第三方工具tomcat-redis-session-manager直接整合即可。對於前端web伺服器(如nginx),做好負載平衡配置,將認證請求分布轉寄給後面多台CAS,實現負載平衡和容錯目的。

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.