Monday, January 09, 2012

SSO with LDAP Spring and CAS

This is a step by step documentation on how to setup a CAS Server and use Single Sign On (SSO) plus Single Sign Off from Spring client applications using LDAP to store authentication and authorization information. CAS support can be enabled or disabled commenting sections of the code.

Our Use Case for this exercise is to have a centralized server that handles user login and logout operations allowing the user to sign in just once and enjoy multiple applications thanks to that centralization.

We then achieve Federation because thanks to SSO via CAS we obtain Authentication while we pull the attributes from LDAP to achieve Authorization. At the end the user is trusted after the first login and the attributes can be obtained from any application.

Before we review Server and Client configuration be sure you understand CAS needs SSL so in order to correctly follow this post you will need to have the CAS Server and two Spring client applications serving on https. SSL needs an IP per domain and if you are running OSX by default you have only one loopback IP ( This means you will need to add other two:
sudo ifconfig lo0 alias up
sudo ifconfig lo0 alias up

Then set in /etc/hosts a domain per loopback IP:
$ vi /etc/hosts

You should be able to get the certificate from each URL and assert they are correct, for example:
$ echo | openssl s_client -connect 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/Downloads/
$ echo | openssl s_client -connect 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/Downloads/
$ echo | openssl s_client -connect 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/Downloads/
$ openssl x509 -text -in ~/Downloads/
$ openssl x509 -text -in ~/Downloads/
$ openssl x509 -text -in ~/Downloads/

Once you are sure the certificates coming from the different URLs are correct (they belong to the specific domain) then you should add them to your keystore:
$ sudo keytool -delete -alias -keystore  /Library/Java/Home/lib/security/cacerts
$ sudo keytool -delete -alias -keystore  /Library/Java/Home/lib/security/cacerts
$ sudo keytool -delete -alias -keystore  /Library/Java/Home/lib/security/cacerts
$ sudo keytool -import -alias -keystore  /Library/Java/Home/lib/security/cacerts -file ~/Downloads/
$ sudo keytool -import -alias -keystore  /Library/Java/Home/lib/security/cacerts -file ~/Downloads/
$ sudo keytool -import -alias -keystore  /Library/Java/Home/lib/security/cacerts -file ~/Downloads/

Of course after you do all the above tomcat must be restarted and if you use Apache for virtual hosting like I explain below you owe to restart it as well.

CAS Server installation and configuration

  1. Download CAS Server from and get cas.war deployed in your server. At this point http://localhost:8080/cas should prompt for credentials. I commonly use apache in front of tomcat so below are the steps using virtual hosts as I have documented before:
    $ mkdir /Users/nestor/Downloads/apache-tomcat-7.0.22/cas
    $ mkdir /Users/nestor/Downloads/apache-tomcat-7.0.22/conf/Catalina/
    $ cp /Users/nestor/Downloads/cas-server-3.4.11/modules/cas-server-webapp-3.4.11.war /Users/nestor/Downloads/apache-tomcat-7.0.22/cas/ROOT.war
  2. Prepare CAS to use LDAP authentication. This is about copying two files to the WEB-INF/lib directory and editing a configuration file in that same directory:
    $ cp /Users/nestor/Downloads/cas-server-3.4.11/modules/cas-server-support-ldap-3.4.11.jar /Users/nestor/Downloads/apache-tomcat-7.0.22/cas/ROOT/WEB-INF/lib 
    $ cp /Users/nestor/Downloads/spring-ldap-1.3.0.RELEASE/dist/spring-ldap-1.3.0.RELEASE-all.jar /Users/nestor/Downloads/apache-tomcat-7.0.22/cas/ROOT/WEB-INF/lib
    $ vi /Users/nestor/Downloads/apache-tomcat-7.0.22/cas/ROOT/WEB-INF/deployerConfigContext.xml
  3. Get spring-security source code


  4. While at this point you should be able to login from the CAS landing page using your LDAP credentials CAS needs to run from an SSL URL in order to be used as Single Sign On (SSO). I use Apache and mod-jk with Tomcat so my SSL configuration is achieved on the Apache side. Here are the steps to generate the self signed certificate and configure Apache virtual host in OSX to serve the CAS service:
    $ cd ~/Downloads/
    $ sudo mkdir /private/etc/apache2/certs/
    $ export
    $ openssl genrsa -des3 -out ${DOMAIN}.key 1024
    $ openssl req -new -key ${DOMAIN}.key -out  ${DOMAIN}.csr
    $ openssl x509 -req -days 365 -in ${DOMAIN}.csr -signkey ${DOMAIN}.key -out ${DOMAIN}.crt
    $ openssl rsa -in ${DOMAIN}.key -out ${DOMAIN}.key
    $ sudo cp ${DOMAIN}.key /private/etc/apache2/certs/
    $ sudo cp ${DOMAIN}.crt /private/etc/apache2/certs/
    $ sudo vi /private/etc/apache2/extra/httpd-vhosts-ssl.conf
    Listen 443
    AddType application/x-x509-ca-cert .crt
    AddType application/x-pkcs7-crl    .crl
    SSLPassPhraseDialog  builtin
    SSLSessionCache        "shmcb:/private/var/run/ssl_scache(512000)"
    SSLSessionCacheTimeout  300
    SSLMutex  "file:/private/var/run/ssl_mutex"
    DocumentRoot "/usr/docs/"
    ErrorLog "/private/var/log/apache2/"
    TransferLog "/private/var/log/apache2/"
    SSLEngine on
    SSLCertificateFile "/private/etc/apache2/certs/"
    SSLCertificateKeyFile "/private/etc/apache2/certs/"
        Options +FollowSymLinks
        AllowOverride All
        Order deny,allow
        Allow from all
    JkMount "/*" nestorurquiza-app1
    $ sudo vi /etc/apache2/httpd.conf
    LoadModule ssl_module libexec/apache2/
    Include /private/etc/apache2/extra/httpd-vhosts-ssl.conf
    $ sudo apachectl configtest
    $ sudo apachectl graceful
  5. Logout and login from SSL. Note that you do not get any alerts stating SSO won't work:
  6. Change the look and feel for the CAS login page editing the file /WEB-INF/view/jsp/default/ui/casLogoutView.jsp and dependencies like the logo from /css/cas.css #header background url and #header h1#app-name background color
  7. Further customize the login page for example as I needed to provide a forgot password functionality I came up with the below implementation in casLogoutView.jsp:
    <a href="<c:out value="${schema}://${domain}/forgotPassword"/>">Forgot Password</a>
  8. Import the client website certificate in the CAS server for example:
    $ echo | openssl s_client -connect 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/Downloads/
    $ sudo keytool -import -alias -keystore  /Library/Java/Home/lib/security/cacerts -file ~/Downloads/

Spring CAS Client

  1. Import the CAS certificate in the client
    $ echo | openssl s_client -connect 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/Downloads/
    $ sudo keytool -import -alias -keystore  /Library/Java/Home/lib/security/cacerts -file ~/Downloads/
  2. Declare some properties in the classpath of the client application:
    # CAS
  3. Then configure the client to perform the Single Sign On and Single Sign Off through CAS. Here is the Spring security configuration for such a use case:
  4. A simple JSP View for the client application Logout Controller
    <%@ include file="/WEB-INF/jsp/includes.jsp" %>
    <%@ include file="/WEB-INF/jsp/header.jsp" %>

    <%@ include file="/WEB-INF/jsp/footer.jsp" %>

  5. The LogoutController
    package com.nestorurquiza.web;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.servlet.ModelAndView;
    public class LogoutController extends RootController {
        public ModelAndView welcomeHandler(HttpServletRequest request,
                HttpServletResponse response) {
            return getModelAndView(ctx, "logout", null);
  6. The logout link in JSP:

  7. At least one dependency is needed in the client application:

  8. At this point hitting any client application URL (for example will redirect the user to the CAS login URL passing the service parameter which contains the encoded servciceURL ( When the user provides valid credentials CAS will redirect her back to the j_spring_cas_security_check client resource and Spring will take care to pull from the session the originally requested URL. When the user clicks on the logout URL ( the CAS server will invalidate the security token and the user will return to a URL where a custom Logout Controller invalidates the Spring Security Context. At this point any attempt to access a protected resource will result in a redirect to the CAS login page as explained.

  9. Probably you will need in your custom code to do extra logic with the saved request (the original requested URL). In Spring 3.0.3 at least you use the below snippet to gain access to it:
    SavedRequest savedRequest = new DefaultSavedRequest(request, new PortResolverImpl());
    if( savedRequest != null ) {
      String redirectUrl = savedRequest.getRedirectUrl();

  10. Clearly you can proceed with the final step from the Server and the steps from the Client to configure extra applications (CAS Services). When you logout from one application or directly from CAS interface you will be logged out from all applications that are integrated with CAS (Single Sign Out or Single Sign Off functionality)

Other than a little issue I reported here the whole integration went smooth. CAS is well supported in Spring and both Single Sign On and Single Sign Off will work out of the box.

By default CAS is shipped with a 4 hours expiration ticket expiration policy. This can be configured as explained in the documentation


ariel said...

Can you provide us with the source code for this sample? Thxs

Nestor Urquiza said...

@ariel Sorry no complete source code can be provided however any Spring project should be able to be adapted to work with CAS and LDAP in order to provide SSO. Perhaps you can open a project in github cloning a couple of the Spring demo projects to provide SSO integration between them.