Spring 3 + Spring Security 3 + CAS 3.3.4 integration

JA-SIG Central Authentication Service (CAS) is an enterprise level, open-source, single sign on solution with a Java server component and various client libraries written in a multitude of languages including PHP, PL/SQL, Java, .Net, PHP, Perl and more.

Both Spring Security 3.0 and Spring 3.0 where released this month. Spring Security provides excelent integration support for CAS. In Spring Security 3.0 a couple of CAS integration components have been changed/renamed. The configuration is also a bit different from Spring Security 2.0.x.

How CAS works

The CAS Server webapp should be deployed on an application server. Applications can use the CAS Server for the authentication process. CAS only provides authentication and no authorisation. The authorisation should be implemented (using Spring Security) by the applications using the CAS Server.

How Spring Security fits in

Lets say an user tries to access a protected resource within an application using Spring Security. Spring Security intercepts the request and checks if the user should be authenticated. If so, the user is forwarded tot the CAS login page. The users typically enters his username and password and submits it to the CAS server. If the user was successfully authenticated by CAS, the users will be redirected back to the application where it was accessing a protected resource. The redirect URL now contains a ticket generated by CAS. Spring Security will use this ticket to validate against CAS if the ticket is valid for this user. If so, the user details will be loaded by Spring Security. If the user is also authorised to access the protected resource, access will be granted.

Configuration

I have a Jasig CAS demo application running and tried to update it to the latest Spring+Security version. I was struggling to get the CAS integration working with Spring Security 3.0. The reference documentation for Spring Security actually contains a few small errors.

For my demo I use the CAS server webapp version 3.3.4. I have it deployed on Apache Tomcat 6.0.20. In the demo I access the CAS application using HTTP, but this should be HTTPS in a production environment! I have deployed the applications using the following URLs:

  • The CAS Server web application: http://localhost:8080/cas-server-webapp-3.3.4/
  • The application using CAS: http://localhost:8080/spring-security-cas/
  • Maven2 dependencies

    I use Maven2 for managing my dependencies, the following libraries should be added to the pom.xml.

    <dependencies>
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-core</artifactId>
    		<version>3.0.0.RELEASE</version>
    		<optional>false</optional>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-webmvc</artifactId>
    		<version>3.0.0.RELEASE</version>
    		<optional>false</optional>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.security</groupId>
    		<artifactId>spring-security-core</artifactId>
    		<version>3.0.0.RELEASE</version>
    		<optional>false</optional>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.security</groupId>
    		<artifactId>spring-security-config</artifactId>
    		<version>3.0.0.RELEASE</version>
    		<scope>compile</scope>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.security</groupId>
    		<artifactId>spring-security-cas-client</artifactId>
    		<version>3.0.0.RELEASE</version>
    		<optional>false</optional>
    	</dependency>
    </dependencies>
    

    Spring Security configuration

    <?xml version="1.0"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
    
    	<!--
    		Enable security, let the casAuthenticationEntryPoint handle all intercepted urls.
    		The CAS_FILTER needs to be in the right position within the filter chain.
    	-->
    	<security:http entry-point-ref="casAuthenticationEntryPoint" auto-config="true">
    		<security:intercept-url pattern="/**" access="ROLE_USER"></security:intercept-url>
    		<security:custom-filter position="CAS_FILTER" ref="casAuthenticationFilter"></security:custom-filter>
    	</security:http>
    
    	<!--
    		Required for the casProcessingFilter, so define it explicitly set and
    		specify an Id Even though the authenticationManager is created by
    		default when namespace based config is used.
    	-->
    	<security:authentication-manager alias="authenticationManager">
    		<security:authentication-provider ref="casAuthenticationProvider"></security:authentication-provider>
    	</security:authentication-manager>
    
    	<!--
    		This section is used to configure CAS. The service is the
    		actual redirect that will be triggered after the CAS login sequence.
    	-->
    	<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
    		<property name="service" value="http://localhost:8080/spring-security-cas/j_spring_cas_security_check"></property>
    		<property name="sendRenew" value="false"></property>
    	</bean>	
    
            <!--
    		The CAS filter handles the redirect from the CAS server and starts the ticket validation.
    	-->
    	<bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
    		<property name="authenticationManager" ref="authenticationManager"></property>
    	</bean>
    
    	<!--
    		The entryPoint intercepts all the CAS authentication requests.
    		It redirects to the CAS loginUrl for the CAS login page.
    	-->
    	<bean id="casAuthenticationEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
    		<property name="loginUrl" value="http://localhost:8080/cas-server-webapp-3.3.4/login"></property>
    		<property name="serviceProperties" ref="serviceProperties"></property>
    	</bean>
    
    	<!-- 
    		Handles the CAS ticket processing.
    	 -->
    	<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
    		<property name="userDetailsService" ref="userService"></property>
    		<property name="serviceProperties" ref="serviceProperties"></property>
    		<property name="ticketValidator">
    			<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
    				<constructor-arg index="0" value="http://localhost:8080/cas-server-webapp-3.3.4"></constructor>
    			</bean>
    		</property>
    		<property name="key" value="cas"></property>
    	</bean>
    
    	<!-- 
    		The users available for this application.
    	-->
    	<security:user-service id="userService">
    		<security:user name="user" password="user" authorities="ROLE_USER"></security:user>
    	</security:user-service>
    </beans>
    

    web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    	id="WebApp_ID" version="2.5">
    	<display-name>webapp</display-name>
    	
    	<context-param>
    		<param-name>contextConfigLocation</param-name>
    		<param-value>/WEB-INF/applicationContext-security.xml</param-value>
    	</context-param>
    	<listener>
    		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    	</listener>
    	
    	<filter>
    		<filter-name>springSecurityFilterChain</filter-name>
    		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    	</filter>
    	<filter-mapping>
    		<filter-name>springSecurityFilterChain</filter-name>
    		<url-pattern>/*</url-pattern>
    	</filter-mapping>
    
    	<welcome-file-list>
    		<welcome-file>index.html</welcome-file>
    		<welcome-file>index.jsp</welcome-file>
    	</welcome-file-list>
    </web-app>
    

    Other resources

    Matt Flemming has some excellent diagrams on how Spring security and CAS collaborate, check out this page: http://mattfleming.com/node/269

    36 Responses to “Spring 3 + Spring Security 3 + CAS 3.3.4 integration”

    1. akash says:

      Can you let me know if it is possible to implement CAS-Spring combination without a UserService bean? I redirect my users to CAS to do the authentication(where CAS talks to my custom DB). In the url intercepts, I just need a way to say that all authenticated users can access that particular URL.

      Is there a way to achieve this without assigning a UserdetailsService and thereby assigning arbitrary roles as all I want is just CAS to authenticate and allow/deny based on that authentication alone(and not based on user roles).

    2. soudmaijer says:

      Hi akash,

      as far as I know you need to specify a UserDetailService. This is required for Spring Security to load the roles for the current user (after CAS authentication). As described in: http://static.springsource.org/spring-security/site/docs/3.0.x/reference/cas.html

      If you just want to grant a user access if the CAS ticket is valid, then you could create your own UserDetailService + UserDetails object. If you want an example I can provide it to you.

      Stephan

      • akash says:

        Hi Stephan,

        Yes, an example would be of immense help.

        I’m currently thinking of having a custom UserDetails implementation which just assigns an arbitrary role(eg ROLE_USER). This way, I also provide the ability to modify the implementation to have custom roles and modify the filters accordingly.

        Let me know your thoughts on this.

        Thanks,
        Akash

    3. soudmaijer says:

      Hi Akash, this sounds like a reasonable solution.

      Typically you provide a UserDetailsService for granting users application specific roles. Iif you dont have any, just assign all authenticated users one logical role like ROLE_AUTHENTICATED for example.

      An example UserDetailsService:

      The UserDetails:

      public class MyUserDetails implements Serializable, UserDetails {
      
      	private static final long serialVersionUID = 1L;
      	private final String username;
      	
      	public MyUserDetails(String username) {
      		this.username=username;
      	}
      	
      	@Override
      	public Collection<GrantedAuthority> getAuthorities() {
      		List<GrantedAuthority> l = new ArrayList<GrantedAuthority>();
      		l.add( new GrantedAuthority() {
      			private static final long serialVersionUID = 1L;
      			
      			@Override
      			public String getAuthority() {
      				return "ROLE_AUTHENTICATED";
      			}
      		});
      		return l;
      	}
      
      	@Override
      	public String getPassword() {
      		return null;
      	}
      
      	@Override
      	public String getUsername() {
      		return username;
      	}
      
      	@Override
      	public boolean isAccountNonExpired() {
      		return true;
      	}
      
      	@Override
      	public boolean isAccountNonLocked() {
      		return true;
      	}
      
      	@Override
      	public boolean isCredentialsNonExpired() {
      		return true;
      	}
      
      	@Override
      	public boolean isEnabled() {
      		return true;
      	}
      }
      

      And offcourse you should modify the Spring configuration accordingly:

      
      <security:intercept-url pattern="/**" access="ROLE_AUTHENTICATED"></security:intercept-url>
      
      <bean id="allAuthenticatedUserService" class="...MyUserService"/>
      
      <bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
          <property name="userDetailsService" ref="allAuthenticatedUserService"/>
          ...
      
      

      Hope this was usefull for you!

    4. akash says:

      Hi Stephan,

      I created my own UserDetailsServiceImpl on similar lines to the one you provided.
      Works great! Thanks a ton!

      I’m planning to have CAS authenticate employee id and password and before the roles are assigned I would like to get the username based on the emp id and then redirect the user to the page from which he initiated the login.

      Is there a simple way to configure a login handler to accomplish what I’m attempting? One approach that I thought of was to accomplish redirects via the service url but not sure if the landing page will be the one from which the user hits login.

    5. akash says:

      In relation to my earlier question, is it possible to dynamically change the service parameter to redirect me(after CAS validation) to the current page from which the login process was initiated?

      • soudmaijer says:

        Hi Akash,

        I will have to look into this, first thing tomorrow ;-)

      • akash says:

        Thanks Stephan.

        I’ve currently implemented what I wanted to achieve by implementing a custom AuthenticationSuccessHandler for my CasAuthenticationFilter. This is kind of kludgy given that I’m dependent on session variables which store the source page URL(which initiated the login).

        Looking forward to your thoughts(and hopefully an elegant solution ;-) ).

      • soudmaijer says:

        Akash,

        let me see if I understand the problem. You want to be redirected back to the page where the login was initiated? So when a user access a protected resource e.g. /mywebapp/some/url.ext. After the login the user should return to this resource right?

        In my setup this is done automatically by Spring Security.

        There is a class called SavedRequestAwareAuthenticationSuccessHandler which you might want to have a look at.

        Most of the login success handling logic is in the AbstractAuthenticationTargetUrlRequestHandler.

    6. akash says:

      Hi Stephan

      My requirement is slightly different. If a user is on a page myapp.com/page1.jsp and he clicks on Login(which is part of the header and thus present in all pages), he would be redirected to CAS and on successful Auth, he would return to page1.jsp. In essence, the login link would not have one specific landing page. I would like the service to do this redirect by changing the serviceUrl dynamically instead of j_spring_cas_security_check.

    7. akash says:

      Hi Stephan,

      I have a few secured and a few unsecured pages in my app and all pages have a login/logout toggle based on the user’s authentication with CAS. I want to implement a feature where my app checks if the user has a valid ticket, then he should be logged in as the user and if not he will be logged in as an anonymous user. This should happen only when he accesses a non-secured page. In case of a secured page, he will be redirected to CAS Login if he is not authenticated.

      I’m not sure how this can be implemented in the current set-up. Please let me know your thoughts on this.

    8. Shriram says:

      Hi

      What are the changes required if the same CAS+Spring setup needs to be achieved for Apache Httpd with tomcat instances.

      • soudmaijer says:

        Hi, Shiram,

        you can run CAS, which just is a webapplication, on Tomcat. Spring runs within a Web or Enterprise application. If you use Spring in an application using war packaging, you can deploy the application on Tomcat.

        If you want to use an Apache HTTP server in front of Tomcat, which is the recommended approach, then you must setup mod_jk to connect Apache to Tomcat via an AJP connector.

        Let me know if you need more information.

    9. Shriram says:

      Thanks soudmaijer for reply,

      I have used the above configuration and the login seems to be ok which means it would bring the cas login and after authenticated it would bring the secured url as configured in spring security.

      Now how do i integrate the casProcessingFilter which has information about failed url, in other words hot to configure failed URL, logout & session time out.

      In some cases I have also seen configuration of casSingleSignOutFilter, not sure if this needs to be explicity configured or not.

    10. misterpom says:

      Thanks for the articl!

      Can you only tell me 1 thing please….what maven repo can i use to retrieve the spring security artifacts?

      I searched several Spring repositories but i cant seem to find the right one where to download the poms. I’m looking in http://s3browse.springsource.com/browse/maven.springframework.org/milestone for example but there are no releases…only release candidates.

      Thanks in advance.

    11. Shadow says:

      Hello,

      Thank you for your help. Could you provide a link to the application that you used for testing? or something similar ? I’m trying to implement CAS 3.4.6 with Spring 3.0.5, from your link and i’m not getting the desired result, that is, i can access the “restricted” page freely without being challenged for my credentials, even if i am using the configuration you posted. I don’t get any errors so I don’t know what i am doing wrong. Hope you can help me.

      On the other hand i was able to restrict access to the example pages provided in Tomcat 7.0.5 by using a filter provided in the jasig tutorials

      CAS Filter
      edu.yale.its.tp.cas.client.filter.CASFilter

      edu.yale.its.tp.cas.client.filter.loginUrl
      https://localhost:8443/cas/login

      edu.yale.its.tp.cas.client.filter.validateUrl
      https://localhost:8443/cas/serviceValidate

      edu.yale.its.tp.cas.client.filter.serverName
      localhost:8443

      CAS Filter
      /servlet/HelloWorldExample

      Is there any way that could help me integrate authorization from spring or any other provider with this type of filter from the web.xml file ?
      Thank you in advance.

    12. R84 says:

      Hi Soudmaijer,

      I would like to know if it is possible to configure Spring Security with SSO Servers other than CAS. An example will be really appreciated.

      Thank you in Advance.

      • soudmaijer says:

        What SSO server are you interested in?

      • R84 says:

        Well I have a task that consists of integrating Barracuda WAF (as SSO) with Spring Secuity using an LDAP Server.

      • R84 says:

        Hi Soudmaijer,

        Don’t you have any idea about how to implement Spring Security with Barracuda WAF server? If not could you provide me with a simple implementation example of a different SSO Server (other than CAS).

        Thank you.

      • soudmaijer says:

        You need to know how the Barracuda solution works. If it uses a ticket based system, like CAS does, then you can create your own SpringSecurity Filter that validates the ticket against Barracuda. I dont know Barracuda but if you want to know more just add reply here and I will contact you by email.

    13. R84 says:

      Thank you for your reply, you can contact me on richard.habchi@gmail.com.

    14. Ankit says:

      hi,

      Thanks for article, but after following all steps, it takes me in infinite loop.?
      Can you please help in this.

    15. Rajaneesh says:

      Hi,

      We have the following requirement..
      We should authenticate the users of our application using CAS. However, if CAS Server is down, we need to use LDap for authentication.

      Is it possible to achieve it. If so, can you please let me know.

      Thanks in advance..

      • soudmaijer says:

        Yes, thats possible. Just add 2 filters to your Spring Security configuration. But typically you handle these types of chains in CAS.

    16. Murugan says:

      Hi all, can anyone can help me to get a sample end to end application on CAS with Spring integration. please with full code.

    17. Sanjay Kharwar says:

      Hi Matt,

      I have CAS protected web based Grails application. This Application is properly working with CAS server(Using browser). There are some JSON based services which are also protected with CAS.

      Now My Team want to make Android Native application using my CAS protected services.

      My Setting:
      1.Tomcat Server 6.0
      2. Configured SSL and CAS server.
      3. Configured application with CAS server(I have used “spring-security-cas” version “1.0.2″ grails plugin for CAS integration)

      Problems:
      1. How to authenticate user in my native application means find the valid token/ticket ?
      2. How to call different other JSON based services without relogin?

      Please suggest me idea or links

      Regards,
      Sanjay Kharwar

    18. chandrakala says:

      Hi,

      I want to develop Single sign on CAS based application. How can i configure CAS server using LDAP authentication. Please provide me example.

      Regards,
      chandrakala

    19. Chary says:

      Hi Stephan,

      Thanks very much! for this article. It really helped.

      Best Regards,
      Chary

    20. Ha P. says:

      Thank for your article, I’m considering both CAS and JOSSO, after your article, I will go for CAS.

    21. Pachu says:

      I am using spring security with cas.But i can’t apply password encoding.Is it not possible?.Can you please provide some information about that.

    22. John says:

      hi,

      I am trying to load test an application which uses CAS authentication with Spring security configuration. We get an authentication error when trying to play the load script. Is there any way you could provide us with some tips on how modifying our script? maybe what to parameterize, etc?

      thanks.