Standalone Java CAS Client

Standalone Java CAS Client

There's a variety of clients for CAS. The Java-based clients (JA-SIG, Yale, see JA-SIG website) typically handle the browser-based client interaction with CAS very well through ServletFilter implementations.

Now what about programmatic authentication, i.e. achieving authentication through non-browser based applications? There exists a CAS .NET client but I did not manage to find the appropriate Java implementation. So here goes - it is based on the Apache HttpClient.

In case I missed any existing implementation achieving the same purpose, let's look at the bright side: at least now I understand the CAS protocol :-)

My CAS client works within any application. It uses the HttpClient and behaves like a browser client as CAS requires cookie support.

Here's the code:
import  org.apache.commons.httpclient.Header;
import  org.apache.commons.httpclient.HttpClient;
import  org.apache.commons.httpclient.HttpMethod;
import  org.apache.commons.httpclient.HttpStatus;
import  org.apache.commons.httpclient.methods.GetMethod;
import  org.apache.commons.httpclient.methods.PostMethod;
import  org.apache.log4j.Logger;

/**
* The CasClient allows users to programmatically login
* to CAS protected services based on the CAS 2 protocol.
* This client behaves like a browser-client in terms of
* cookie handling.<br>
*
@author  Mathias Richter
*/
public   class  CasClient
{
  
   
public   static  Logger LOG  =  Logger.getLogger( CasClient. class   );

   
public   static   final  String LOGIN_URL_PART  =   " login " ;
   
public   static   final  String SERVICE_VALIDATE_URL_PART  =   " serviceValidate " ;
   
public   static   final  String TICKET_BEGIN  =   " ticket= " ;
   
private   static   final  String LT_BEGIN  =   " name= " lt "  value= "" ;
    public   static   final  String CAS_USER_BEGIN  =   " <cas:user> " ;
   
public   static   final  String CAS_USER_END  =   " </cas:user> " ;
  
   
private  HttpClient fClient;
   
private  String fCasUrl;
  
   
/**
    * Construct a new CasClient.
    *
    * 
@param  casUrl The base URL of the CAS service to be used.
    
*/
   
public  CasClient( String casBaseUrl )
   {
       
this new  HttpClient(), casBaseUrl );
   }
  
   
/**
    * Construct a new CasClient which uses the specified HttpClient
    * for its HTTP calls.
    *
    * 
@param  client
    * 
@param  casBaseUrl
    
*/
   
public  CasClient( HttpClient client, String casBaseUrl )
   {
       fClient 
=  client;
       fCasUrl 
=  casBaseUrl;
   }
  
   
/**
    * Authenticate the specified username with the specified password.
    * This will not yield any ticket, as no service is authenticated
    * against. This wil just set the CAS cookie in this client upon
    * successful authentication.
    *
    * 
@param  username
    * 
@param  password
    
*/
   
public   void  authenticate( String username, String password )
   {
       authenticate( 
null , username, password );
   }
  
   
/**
    * Validate the specified service ticket against the specified service.
    * If the ticket is valid, this will yield the clear text user name
    * of the autenticated user.<br>
    * Note that each service ticket issued by CAS can be used exactly once
    * to validate.
    *
    * 
@param  serviceUrl
    * 
@param  serviceTicket
    *
    * 
@return  Clear text username of the authenticated user.
    
*/
   
public  String validate( String serviceUrl, String serviceTicket )
   {
       String result 
=   null ;
       PostMethod method 
=   new  PostMethod( fCasUrl  +  SERVICE_VALIDATE_URL_PART );
       method.setParameter( 
" service " , serviceUrl );
       method.setParameter( 
" ticket " , serviceTicket );
       
try
       {
           
int  statusCode  =  fClient.executeMethod(method);
           
if  (statusCode  !=  HttpStatus.SC_OK)
           {
               LOG.error( 
" Could not validate:  "   +  method.getStatusLine() );
               method.releaseConnection();
           } 
else
           {   
               result 
=  extractUser(  new  String( method.getResponseBody() ) );
           }
       } 
catch  ( Exception x )
       {
           LOG.error( 
" Could not validate:  "   +  x.toString () );
           x.printStackTrace();
       }
       method.releaseConnection();
       
return  result;
   }
  
   
/**
    * Authenticate the specified user with the specified password against the
    * specified service.
    *
    * 
@param  serviceUrl May be null. If a url is specified, the authentication will happen against this service, yielding a service ticket which can be validated.
    * 
@param  username
    * 
@param  password
    * 
@return  A valid service ticket, if and only if the specified service URL is not null.
    
*/
   
public  String authenticate( String serviceUrl, String username, String password )
   {
       String lt 
=  getLt( serviceUrl );
       
if  ( lt  ==   null  )
       {
           LOG.error( 
" Cannot retrieve LT from CAS. Aborting authentication for ' "   +  username  +   " ' "  );
           
return   null ;
       }
       String result 
=   null ;
       PostMethod method 
=   new  PostMethod( fCasUrl  +  LOGIN_URL_PART );
       
if  ( serviceUrl  !=   null  )  //  optional
           method.setParameter(  " service " , serviceUrl );
       method.setParameter( 
" _eventId " " submit "  );
       method.setParameter(
" username " , username );
       method.setParameter(
" password " , password );
       method.setParameter(
" lt " , lt );
       method.setParameter( 
" gateway " " true "  );
       
try
       {
           fClient.executeMethod(method);
           
if  ( serviceUrl  ==   null  )
           {
               
if  ( extractLt(  new  String( method.getResponseBody() ) )  !=   null  )  //  if CAS does not return a login page with an LT authentication was successful
               {
                   LOG.error( 
" Authentication for ' "   +   username  +   " ' unsuccessful "  );
                   
if  ( LOG.isDebugEnabled() )
                       LOG.debug( 
" Authentication for ' "   +  username  +   " ' unsuccessful. "  );
               } 
else
               {
                   
if  ( LOG.isDebugEnabled() )
                       LOG.debug( 
" Authentication for ' "   +  username  +   " ' unsuccessful. "  );
               }
           } 
else
           {
               Header h 
=  method.getResponseHeader(  " Location "  );
               
if  ( h  !=   null  )
                   result 
=  extractServiceTicket( h.getValue() );
               
if  ( result  ==   null  )
                   LOG.error( 
" Authentication for ' "   +  username  +   " ' unsuccessful. "  );
           }
       } 
catch  ( Exception x )
       {
           LOG.error( 
" Could not authenticate' "   +  username  +   " ': "   +  x.toString () );
       }
       method.releaseConnection();
       
return  result;
   }
  
   
/**
    * Helper method to extract the user name from a "service validate" call to CAS.
    *
    * 
@param  data Response data.
    * 
@return  The clear text username, if it could be extracted, null otherwise.
    
*/
   
protected  String extractUser( String data )
   {
       String user 
=   null ;
       
int  start  =  data.indexOf( CAS_USER_BEGIN  );
       
if  ( start  >=   0  )
       {
           start 
+=  CAS_USER_BEGIN.length();
           
int  end  =  data.indexOf( CAS_USER_END );
           
if  ( end  >  start )
               user 
=  data.substring( start, end );
           
else
               LOG.warn( 
" Could not extract username from CAS validation response. Raw data is: ' "   +  data  +   " ' "  );
       } 
else
       {
           LOG.warn( 
" Could not extract username from CAS validation response. Raw data is: ' "   +  data  +   " ' "  );
       }
       
return  user;
   }
  
   
/**
    * Helper method to extract the service ticket from a login call to CAS.
    *
    * 
@param  data Response data.
    * 
@return  The service ticket, if it could be extracted, null otherwise.
    
*/
   
protected  String extractServiceTicket( String data )
   {
       String serviceTicket 
=   null ;
       
int  start  =  data.indexOf( TICKET_BEGIN  );
       
if  ( start  >   0  )
       {
           start 
+=  TICKET_BEGIN.length();
           serviceTicket 
=  data.substring( start );
       }
       
return  serviceTicket;
   }
  
   
/**
    * Helper method to extract the LT from a login form from CAS.
    *
    * 
@param  data Response data.
    * 
@return  The LT, if it could be extracted, null otherwise.
    
*/
   
protected  String extractLt( String data )
   {
       String token 
=   null ;
       
int  start  =  data.indexOf( LT_BEGIN  );
       
if  ( start  <   0  )
       {
           LOG.error( 
" Could not obtain LT token from CAS: LT Token not found in response. "  );
       } 
else
       {
           start 
+=  LT_BEGIN.length();
           
int  end  =  data.indexOf(  """ , start );
           token  =  data.substring( start, end );
       }       
       
return  token;
   }
  
   
/**
    * This method requests the original login form from CAS.
    * This form contains an LT, an initial token that must be
    * presented to CAS upon sending it an authentication request
    * with credentials.<br>
    * If a service URL is provided (which is optional), this method
    * will post the URL such that CAS authenticates against the
    * specified service when a subsequent authentication request is
    * sent.
    *
    * 
@param  serviceUrl
    * 
@return  The LT token if it could be extracted from the CAS response.
    
*/
   
protected  String getLt( String serviceUrl )
   {
       String lt 
=   null ;
       HttpMethod method 
=   null ;
       
if  ( serviceUrl  ==   null  )
           method 
=   new  GetMethod( fCasUrl  +  LOGIN_URL_PART );
       
else
       {
           method 
=   new  PostMethod( fCasUrl  +  LOGIN_URL_PART );
           ( ( PostMethod ) method ).setParameter( 
" service " , serviceUrl );
       }
       
try
       {
           
int  statusCode  =  fClient.executeMethod(method);
           
if  (statusCode  !=  HttpStatus.SC_OK)
           {
               LOG.error( 
" Could not obtain LT token from CAS:  "   +  method.getStatusLine() );
               method.releaseConnection();
           } 
else
           {
               Object o 
=  method.getResponseHeaders() ;
               
return  extractLt(  new  String( method.getResponseBody() ) );
           }
       } 
catch  ( Exception x )
       {
           LOG.error( 
" Could not obtain LT token from CAS:  "   +  x.toString () );
       }
       method.releaseConnection();
       
return  lt;
   }
  
}

你可能感兴趣的:(Standalone Java CAS Client)