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:
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
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).
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
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
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!
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.
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?
Hi Akash,
I will have to look into this, first thing tomorrow
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
).
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.
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.
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.
Hi
What are the changes required if the same CAS+Spring setup needs to be achieved for Apache Httpd with tomcat instances.
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.
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.
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.
Hi misterpom,
the Spring Security artifacts are in the maven central repo`s now. Check http://repo2.maven.org/maven2/org/springframework/security/spring-security-core/3.0.5.RELEASE/
Regards, Stephan
Thanks & best regards!
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.
the Filter is the one from http://wiki.jasig.org/display/CASUM/Demo
Looks like anti SQL-injection code tricked me, and deleted the tags
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.
What SSO server are you interested in?
Well I have a task that consists of integrating Barracuda WAF (as SSO) with Spring Secuity using an LDAP Server.
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.
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.
Thank you for your reply, you can contact me on richard.habchi@gmail.com.
hi,
Thanks for article, but after following all steps, it takes me in infinite loop.?
Can you please help in this.
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..
Yes, thats possible. Just add 2 filters to your Spring Security configuration. But typically you handle these types of chains in CAS.
Hi all, can anyone can help me to get a sample end to end application on CAS with Spring integration. please with full code.
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