Java HttpComponents Based Test Class

New CAS documentation site

CAS documentation has moved over to apereo.github.io/cas, starting with CAS version 4.x. The wiki will no longer be maintained. For the most recent version of the documentation, please refer to the aforementioned link.

The Java class source attached to this page (edu.yale.its.cas.test.Logon) uses the Apache HttpComponents library from http://hc.apache.org to obtain login and service tickets from a CAS server. It is intended as a stress tester and is not currently designed to test all the CAS options, although if you look at the code you can see how things work and adjust it. An example of its use is provided in the Test class:

 public static void main(String[] args) throws CasFailException {
        
        Logon.debug = true;     // display status and headers returned
        Logon.showPage = false; // Do not print the page unless an error is returned.
        
        for (int j=0;j<NUMLOGINS;j++) {
            // The login object
            Logon cas = new Logon("https://securedev.its.yale.edu:8440/cas");
            // A separate ST validate object (just because it can be done)
            Logon casv = new Logon(https://securedev.its.yale.edu:8441/cas);
            // Add service= on the end of the /login
            cas.setService("http://www.yale.edu");
            cas.authenticate(args[0], args[1]);
            // Validate the ST returned with the redirect
            cas.validate();
            cas.setService("http://www.foo.yale.edu");
            casv.setService("http://www.foo.yale.edu");
            for (int i=0;i<NUMST;i++) {
                String st = cas.casServiceTicket();
                casv.validate(st,null);
            }
        }
    }

As the comments in the Logon class indicate, an instance of the class encapsulates a single login to CAS with a userid and password (if you want to logon a second time, create a new instance of the class). The Logon class saves the TGT cookie internally and the most recently used service string and most recently returned service ticket. You must provide the URL of the CAS server to the constructor.

The authenticate method logs on to CAS with a supplied userid and password (provided here by the arguments to the command). If you do not authenticate, you can call the validate() method to validate a ST with this object, but the ST has to be generated by a different object that did authenticate. In this example, one object logs on and generates STs while the second object validates them to a second instance of CAS in a clustered environment using a single test machine (to test the TicketRegistry replication across the cluster).

You can pass the service and ST strings as arguments, or if you are going to use the same values over and over you can call the setService method and omit or pass null for the service name. Look at the JavaDoc comments to see when arguments can be omitted. In this example, the ST returned by the initial login is validated by the cas.validate() call with no arguments, which means the object validates the internally saved last ST returned by CAS. However, later on since one object (cas) generates STs and a second object (casv) validates them, the ST has to be passed as an argument, but the service name on validate is set to null which means that it uses the value stored in the casv.setService() call.

To turn this into a complete stress testing system, with multiple threads beating up on CAS concurrently with lots of userids, it may be necessary to learn more about the HttpComponents Connection Manager than I have bothered to include here. The default is a simple single threaded connection manager which is good enough to provide some moderate stress, particularly by generating arbitrary numbers of logon and ST entries in the TicketRegistry (simply omit the validate() calls if you want the number of STs in the cache to start to grow large.

To run this you need httpclient-4.0.1.jar, httpcore.4.0.1.jar, and commons-logging-1.1.jar from Apache.

Disclaimer: During testing I found that CAS was always generating the same value for the name="lt" hidden field variable that holds flow state between writing the form and processing the POST, so I put that value into the code as a literal constant. If this behavior of Spring changes, it would always be possible to scan through the text of the login page that the codes stores but does not bother to inspect to find the actual value= of the corresponding <input> element. I had planned to do that, but then discovered that in practice it was unnecessary.