Category: Spring

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