單點登陸之CAS簡介


ok,現在開始本文的重點內容解說,先來了解一下cas 實現single sign out的原理,如圖所看到的:



 
                                                                            圖一


                                                                      圖二

第一張圖演示了單點登錄的工做原理。
第二張圖演示了單點登出的工做原理。

從第一張圖中。當一個web瀏覽器登陸到應用server時,應用server(application)會檢測用戶的session,假設沒有session,則應用server會把url跳轉到CASserver上,要求用戶登陸,用戶登陸成功後,CASserver會記請求的application的url和該用戶的sessionId(在應用server跳轉url時,經過參數傳給CASserver)。

此時在CASserver會種下TGC Cookie值到webbrowser.擁有該TGCCookie的webbrowser可以無需登陸進入所有創建sso服務的應用serverapplication。

在第二張圖中,當一個web瀏覽器要求登退應用server。應用server(application)會把url跳轉到CAS server上的/cas/logout url資源上,

CAS server接受請求後,會檢測用戶的TCG Cookie。把相應的session清除,同一時候會找到所有經過該TGCsso登陸的應用服務器URL提交請求,所有的回調請求中,包括一個參數logoutRequest,內容格式例如如下:

web

< samlp:LogoutRequest  ID ="[RANDOM ID]"  Version ="2.0"  IssueInstant ="[CURRENT DATE/TIME]" >
< saml:NameID > @NOT_USED@ </ saml:NameID >
< samlp:SessionIndex > [SESSION IDENTIFIER] </ samlp:SessionIndex >
</ samlp:LogoutRequest >


所有收到請求的應用serverapplication會解析這個參數,取得sessionId。依據這個Id取得session後。把session刪除。
這樣就實現單點登出的功能。

知道原理後,如下是結合源碼來說述一下內部的代碼怎麼實現的。
首先,要實現single sign out在 應用serverapplication端的web.xml要增長下面配置
< filter >
   
< filter-name > CAS Single Sign Out Filter </ filter-name >
   
< filter-class > org.jasig.cas.client.session.SingleSignOutFilter </ filter-class >
</ filter >

< filter-mapping >
   
< filter-name > CAS Single Sign Out Filter </ filter-name >
   
< url-pattern >
31       protected   static  SessionMappingStorage getSessionMappingStorage() {
32           return  SingleSignOutFilter.getSessionMappingStorage();
33      }
34  }

接下來,咱們來看一下CAS server端回調是怎麼實現的
先來看一下配置,咱們知道CASserver所有的用戶登陸,登出操做,都是由CentralAuthenticationSer viceImpl對象來管理。
咱們就先把到CentralAuthenticationSer viceImpl的spring配置,在applicationContext.xml文件裏
<!--  CentralAuthenticationService  -->
    
< bean  id ="centralAuthenticationService"  class ="org.jasig.cas.CentralAuthenticationServiceImpl"
        p:ticketGrantingTicketExpirationPolicy-ref
="grantingTicketExpirationPolicy"
        p:serviceTicketExpirationPolicy-ref
="serviceTicketExpirationPolicy"
        p:authenticationManager-ref
="authenticationManager"
        p:ticketGrantingTicketUniqueTicketIdGenerator-ref
="ticketGrantingTicketUniqueIdGenerator"
        p:ticketRegistry-ref
="ticketRegistry"
            p:servicesManager-ref
="servicesManager"
            p:persistentIdGenerator-ref
="persistentIdGenerator"
        p:uniqueTicketIdGeneratorsForService-ref
="uniqueIdGeneratorsMap"   />

配置使用了spring2.0的xsd。

CentralAuthenticationServiceImpl有一個屬性叫uniqueTicketIdGeneratorsForService,它是一個map對象
它的key值是所有實現org.jasig.cas.authentication.principal.Service接口的類名,用於保存Principal對象和進行單點登出回調

application server時使用value值爲org.jasig.cas.util.DefaultUniqueTicketIdGenerator對象,用於生成惟一的TGCticket。
該屬性引用的uniqueIdGeneratorsMap bean在uniqueIdGenerators.xml配置文件裏。
spring

< util:map  id ="uniqueIdGeneratorsMap" >
        
< entry
            
key ="org.jasig.cas.authentication.principal.SimpleWebApplicationServiceImpl"
            value-ref
="serviceTicketUniqueIdGenerator"   />
        
< entry
            
key ="org.jasig.cas.support.openid.authentication.principal.OpenIdService"
            value-ref
="serviceTicketUniqueIdGenerator"   />
        
< entry
            
key ="org.jasig.cas.authentication.principal.SamlService"
            value-ref
="samlServiceTicketUniqueIdGenerator"   />
        
< entry
            
key ="org.jasig.cas.authentication.principal.GoogleAccountsService"
            value-ref
="serviceTicketUniqueIdGenerator"   />
    
</ util:map >

那CentralAuthenticationSer viceImpl是怎麼調用的呢?
咱們跟蹤一下代碼,在建立ticket的方法 public StringcreateTicketGrantingTick et(final Credentials credentials)中
可以找到下面這樣一段代碼:
1           // 建立 TicketGrantingTicketImpl 實例
2               final  TicketGrantingTicket ticketGrantingTicket  =   new  TicketGrantingTicketImpl(
3                   this .ticketGrantingTicketUniqueTicketIdGenerator
4                      .getNewTicketId(TicketGrantingTicket.PREFIX),
5                  authentication,  this .ticketGrantingTicketExpirationPolicy);
6           // 並把該對象保存到 ticketRegistry中
7           this .ticketRegistry.addTicket(ticketGrantingTicket);

上面的代碼,看到ticketRegistry對象保存了建立的TicketGrantingTicketImpl 對象,如下咱們看一下當ticket銷燬的時候。會作什麼
事情,代碼例如如下:
 1       public   void  destroyTicketGrantingTicket( final  String ticketGrantingTicketId) {
 2          Assert.notNull(ticketGrantingTicketId);
 3 
 4           if  (log.isDebugEnabled()) {
 5              log.debug( " Removing ticket [ "   +  ticketGrantingTicketId
 6                   +   " from registry. " );
 7          }
 8       // 從 ticketRegistry對象中。取得TicketGrantingTicket對象
 9           final  TicketGrantingTicket ticket  =  (TicketGrantingTicket)  this .ticketRegistry
10              .getTicket(ticketGrantingTicketId, TicketGrantingTicket. class );
11 
12           if  (ticket  ==   null {
13               return ;
14          }
15 
16           if  (log.isDebugEnabled()) {
17              log.debug( " Ticket found.  Expiring and then deleting. " );
18          }
19          ticket.expire(); // 調用expire()方法,讓ticket過時失效
20           this .ticketRegistry.deleteTicket(ticketGrantingTicketId); // 從ticketRegistry中刪除的ticket 對象
21      }

咱們看到,它是從 ticketRegistry對象中取得 TicketGrantingTicket對象後,調用expire方法。接下來。要關心的就是expire方法作什麼事情
 1       public   synchronized   void  expire() {
 2           this .expired.set( true );
 3          logOutOfServices();
 4      }
 5 
 6       private   void  logOutOfServices() {
 7           for  ( final  Entry < String, Service >  entry  this .services.entrySet()) {
 8              entry.getValue().logOutOfService(entry.getKey());
 9          }
10      }

從代碼可以看到,它是遍歷每個 Service對象,並運行logOutOfService方法。參數是StringsessionIdentifier
現在咱們可以相應中,它存放的Service就是在 uniqueIdGeneratorsMap bean定義中的那些實現類

因爲logOutOfService方法的實現。所有實現類都是由它們繼承的抽象類AbstractWebApplicationSe rvice來實現。咱們來看一下
AbstractWebApplicationSe rvice的logOutOfService方法,就可以終於找出,實現singlesign out的真正實現代碼,如下是主要代碼片斷:

 1     public   synchronized   boolean  logOutOfService( final  String sessionIdentifier) {
 2           if  ( this .loggedOutAlready) {
 3               return   true ;
 4          }
 5 
 6          LOG.debug( " Sending logout request for:  "   +  getId());
 7           // 組裝 logoutRequest參數內容
 8           final  String logoutRequest  =   " <samlp:LogoutRequest xmlns:samlp=\ " urn:oasis:names:tc:SAML: 2.0 :protocol\ "  ID=\ ""
 9               +  GENERATOR.getNewTicketId( " LR " )
10               +   " \ "  Version = \ " 2.0\ "  IssueInstant = \ ""   +  SamlUtils.getCurrentDateAndTime()
11               +   " \ " >< saml:NameID 
12 
13  xmlns:saml = \ " urn:oasis:names:tc:SAML:2.0:assertion\ " > @NOT_USED@ </ saml:NameID >< samlp:SessionIndex > "
14               +  sessionIdentifier  +   " </samlp:SessionIndex></samlp:LogoutRequest> " ;
15          
16           this .loggedOutAlready  =   true ;
17           // 回調所有的application,getOriginalUrl()是取得回調的application url
18           if  ( this .httpClient  !=   null {
19               return   this .httpClient.sendMessageToEndPoint(getOriginalUrl(), logoutRequest);
20          }
21          
22           return   false ;
23      }
至此。已經經過源碼把 CAS實現 single signout的實現原理和方法完整敘述了一遍,但願對CAS感興趣的朋友有所幫忙。
相關文章
相關標籤/搜索