Skip navigation.
Home

How Spring Security hooks to Central Authentication Service (CAS)

CAS High Level Collaboration DiagramOur latest software project used CAS for authentication and single sign-on. I couldn't find any good documentation on how Spring Security and CAS played together. There is some documentation about how to configure your application to use CAS but not much on what messages go back and forth by whom and when.

Normally, we would drop in the CAS war, do a little configuration and use it for single sign-on for a suite of applications. I had a basic understanding of how it worked conceptually, but not really a complete understanding of how it worked in practice. This was okay provided that we didn't run into any issues with authentication or needed to extend CAS functionality.

This understanding gap started to show up when I needed to use Spring Security as the CAS client. I was able able to configure the client easily enough, but couldn't quite get my head around how Spring Security interacted with CAS at a message level. I put together the following diagrams to address that gap.

High Level Collaboration Diagram

This diagram doesn't show the internals of the Spring Security client but it is very helpful if you aren't all that familiar with how CAS works in general. The main takeaways are how involved the browser is in the process. There are a bunch of redirects that happen (really without the user noticing) during an login/authentication. The second thing is step #8. This is the only time where the Application (Spring Security client) talks directly to CAS. This is also why the Application's JVM needs to be accept the SSL certificate of the CAS application. This seems to trip people up.

CAS High Level Collaboration Diagram

Sequence Diagram

This diagram has the three main actors in the Spring Security CAS client and shows which classes actually initiate redirects. This is very helpful if you need to modify the CAS application. I put it together as a first step in determining how to add password expiration detection with force password change into CAS instead of having them in each application.

CAS Sequence Diagram

Matt Fleming's picture

Non-Simple security.xml

This is the real security.xml file that we use to configure Spring Security. Normally we wouldn't need to do this expanded version, but I wanted the channelProcessingFilter to fire (which switches the protocol from http to https) on all non-secured resources. If the concise configuration actually used the filters attribute on the sec:intercept-url element, I wouldn't have to break it all out. It was good to figure out anyway.

-Matt

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd
                        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
 
    <bean id="securityFilter" class="org.springframework.security.util.FilterChainProxy">
        <sec:filter-chain-map path-type="ant">
            <sec:filter-chain pattern="/images/**" filters="channelProcessingFilter"/>
            <sec:filter-chain pattern="/css/**" filters="channelProcessingFilter"/>
            <sec:filter-chain pattern="/js/**" filters="channelProcessingFilter"/>
            <sec:filter-chain pattern="/403.jsp" filters="channelProcessingFilter"/>
            <sec:filter-chain pattern="/404.jsp" filters="channelProcessingFilter"/>
            <sec:filter-chain pattern="/error.jsp" filters="channelProcessingFilter"/>
            <sec:filter-chain pattern="/**/cas/changePassword.htm*" filters="channelProcessingFilter"/>
            <sec:filter-chain pattern="/**/cas/login.htm*" filters="channelProcessingFilter"/>
            <sec:filter-chain pattern="/**/cas/passwordExpired.htm*" filters="channelProcessingFilter"/>
            <sec:filter-chain pattern="/**/*.html*" filters="channelProcessingFilter"/>
            <sec:filter-chain pattern="/**"
                              filters="channelProcessingFilter,httpSessionContextIntegrationFilter,logoutFilter,casSingleSignOutFilter,casProcessingFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor"/>
        </sec:filter-chain-map>
    </bean>
 
    <!-- this is what hooks up the CAS entry point -->
    <bean id="exceptionTranslationFilter" class="org.springframework.security.ui.ExceptionTranslationFilter">
        <property name="authenticationEntryPoint">
            <ref local="casProcessingFilterEntryPoint"/>
        </property>
    </bean>
 
    <!-- where do I go when I need authentication from CAS-->
    <bean id="casProcessingFilterEntryPoint" class="org.springframework.security.ui.cas.CasProcessingFilterEntryPoint">
        <property name="loginUrl" value="https://localhost:5543/cas/login"/>
        <property name="serviceProperties" ref="serviceProperties"/>
    </bean>
 
    <!-- defines which roles are allowed to access http resources -->
    <bean id="filterInvocationInterceptor" class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="accessDecisionManager" ref="accessDecisionManager"/>
        <property name="objectDefinitionSource">
            <value>
                PATTERN_TYPE_APACHE_ANT
                **=ROLE_ALLOWED_ROLES_HERE
            </value>
        </property>
    </bean>
 
    <!-- hooks up CAS ticket validator and user details loader -->
    <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
        <property name="providers">
            <list>
                <ref bean="casAuthenticationProvider"/>
            </list>
        </property>
    </bean>
 
    <!-- supporting class for filterInvocationInterceptor -->
    <bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
        <property name="allowIfAllAbstainDecisions" value="false"/>
        <property name="decisionVoters">
            <list>
                <ref local="roleVoter"/>
            </list>
        </property>
    </bean>
 
    <bean id="roleVoter" class="org.springframework.security.vote.RoleVoter">
        <property name="rolePrefix" value=""/>
    </bean>
 
    <!-- setup method level security using annotations -->
    <sec:global-method-security jsr250-annotations="enabled" secured-annotations="enabled"/>
    <alias name="authenticationManager" alias="_authenticationManager"/>
 
    <bean id="passwordEncoder" class="org.springframework.security.providers.encoding.ShaPasswordEncoder"/>
 
    <!-- which service (application) am I authenticating -->
    <bean id="serviceProperties" class="org.springframework.security.ui.cas.ServiceProperties">
        <property name="service" value="https://localhost:5543/for/j_spring_cas_security_check"/>
        <property name="sendRenew" value="false"/>
    </bean>
 
    <!-- handles a logout request from the CAS server -->
    <bean id="casSingleSignOutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
 
    <!-- performs CAS authentication -->
    <bean id="casProcessingFilter" class="org.springframework.security.ui.cas.CasProcessingFilter">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationFailureUrl" value="/403.jsp"/>
        <property name="alwaysUseDefaultTargetUrl" value="false"/>
        <property name="defaultTargetUrl" value="/"/>
    </bean>
 
    <!-- Does the CAS ticket validation and user details loading -->
    <bean id="casAuthenticationProvider" class="org.springframework.security.providers.cas.CasAuthenticationProvider">
        <property name="userDetailsService" ref="pickYourUserDetailsServiceImplementation"/>
        <property name="serviceProperties" ref="serviceProperties"/>
        <property name="ticketValidator">
            <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <constructor-arg index="0" value="https://localhost:5543/cas"/>
            </bean>
        </property>
        <property name="key" value="my_password_for_this_auth_provider_only"/>
    </bean>
 
    <!-- Log failed authentication attempts to commons-logging -->
    <bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener"/>
 
    <bean id="httpSessionContextIntegrationFilter"
          class="org.springframework.security.context.HttpSessionContextIntegrationFilter"/>
 
    <bean id="securityContextHolderAwareRequestFilter"
          class="org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter"/>
 
    <!-- ===================== SSL SWITCHING ==================== -->
    <bean id="channelProcessingFilter" class="org.springframework.security.securechannel.ChannelProcessingFilter">
        <property name="channelDecisionManager" ref="channelDecisionManager"/>
        <property name="filterInvocationDefinitionSource">
            <value>
                PATTERN_TYPE_APACHE_ANT
                **=REQUIRES_SECURE_CHANNEL
            </value>
        </property>
    </bean>
 
    <bean id="channelDecisionManager" class="org.springframework.security.securechannel.ChannelDecisionManagerImpl">
        <property name="channelProcessors">
            <list>
                <bean class="org.springframework.security.securechannel.SecureChannelProcessor">
                    <property name="entryPoint" ref="channelEntryPoint"/>
                </bean>
                <bean class="org.springframework.security.securechannel.InsecureChannelProcessor">
                    <property name="entryPoint" ref="channelEntryPoint"/>
                </bean>
            </list>
        </property>
    </bean>
 
    <bean id="channelEntryPoint" class="org.springframework.security.securechannel.RetryWithHttpsEntryPoint">
        <property name="portMapper" ref="portMapper"/>
    </bean>
 
    <bean id="portMapper" class="org.springframework.security.util.PortMapperImpl">
        <property name="portMappings">
            <map>
                <entry key="80" value="443"/>
                <entry key="8080" value="8443"/>
                <entry key="5580" value="5543"/>
            </map>
        </property>
    </bean>
 
    <!-- Invoked when the user clicks logout -->
    <bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter">
        <!-- URL redirected to after logout success -->
        <constructor-arg value="https://localhost:5543/cas/logout"/>
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.ui.logout.SecurityContextLogoutHandler">
                    <property name="invalidateHttpSession" value="false"/>
                </bean>
            </list>
        </constructor-arg>
    </bean>
 
</beans>

-Matt

Problem with security.xml

Here it is what I found in my log :

19 janv. 2010 15:20:46 org.apache.catalina.core.StandardContext listenerStart
GRAVE: Exception lors de l'envoi de l'�v�nement contexte initialis� (context initialized) � l'instance de classe d'�coute (listener) org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 10 in XML document from ServletContext resource [/WEB-INF/context/security.xml] is invalid; nested exception is org.xml.sax.SAXParseException: The prefix "sec" for element "sec:http" is not bound.
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:404)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:342)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:310)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
at org.kablink.teaming.spring.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:69)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:92)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:123)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:422)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:352)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3843)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4342)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:791)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:525)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:627)
at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:553)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:488)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1149)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:516)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:578)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
Caused by: org.xml.sax.SAXParseException: The prefix "sec" for element "sec:http" is not bound.
at org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
at org.apache.xerces.util.ErrorHandlerWrapper.fatalError(Unknown Source)
at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
at org.springframework.beans.factory.xml.DefaultDocumentLoader.loadDocument(DefaultDocumentLoader.java:75)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:396)
... 37 more
19 janv. 2010 15:20:46 org.apache.catalina.core.StandardContext listenerStart
GRAVE: Exception lors de l'envoi de l'�v�nement contexte initialis� (context initialized) � l'instance de classe d'�coute (listener) org.kablink.teaming.web.servlet.listener.ContextListener
java.lang.NullPointerException
at org.kablink.teaming.util.SpringContextUtil.getBean(SpringContextUtil.java:117)
at org.kablink.teaming.web.servlet.listener.ContextListener.contextInitialized(ContextListener.java:45)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3843)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4342)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:791)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:525)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:627)
at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:553)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:488)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1149)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:516)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:578)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)

Here is my file security.xml :

Matt Fleming's picture

You need to add the namespace...

Look at the declaration in my xml file. This is where you define the xml namespaces.. in your case you're missing the sec stuff.
-Matt

Thank you Matt. You were

Thank you Matt.
You were right.

Now I have another problem :

GRAVE: Exception lors de l'envoi de l'�v�nement contexte initialis� (context initialized) � l'instance de classe d'�coute (listener) org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_filterChainProxy': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_filterChainList': Cannot resolve reference to bean '_exceptionTranslationFilter' while setting bean property 'filters' with key [2]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_exceptionTranslationFilter': Initialization of bean failed; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.security.ui.cas.CasProcessingFilterEntryPoint] for bean with name 'casProcessingFilterEntryPoint' defined in ServletContext resource [/WEB-INF/context/security.xml]; nested exception is java.lang.ClassNotFoundException: org.springframework.security.ui.cas.CasProcessingFilterEntryPoint
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:480)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:429)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:380)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3843)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4342)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:791)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:525)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:627)
at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:553)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:488)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1149)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:516)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:578)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_filterChainList': Cannot resolve reference to bean '_exceptionTranslationFilter' while setting bean property 'filters' with key [2]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_exceptionTranslationFilter': Initialization of bean failed; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.security.ui.cas.CasProcessingFilterEntryPoint] for bean with name 'casProcessingFilterEntryPoint' defined in ServletContext resource [/WEB-INF/context/security.xml]; nested exception is java.lang.ClassNotFoundException: org.springframework.security.ui.cas.CasProcessingFilterEntryPoint
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:275)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:104)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveManagedList(BeanDefinitionValueResolver.java:287)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:126)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1245)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1010)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:472)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
at org.springframework.security.config.FilterChainProxyPostProcessor.postProcessBeforeInitialization(FilterChainProxyPostProcessor.java:52)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:350)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1331)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
... 38 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_exceptionTranslationFilter': Initialization of bean failed; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.security.ui.cas.CasProcessingFilterEntryPoint] for bean with name 'casProcessingFilterEntryPoint' defined in ServletContext resource [/WEB-INF/context/security.xml]; nested exception is java.lang.ClassNotFoundException: org.springframework.security.ui.cas.CasProcessingFilterEntryPoint
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:480)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:269)
... 56 more
Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.security.ui.cas.CasProcessingFilterEntryPoint] for bean with name 'casProcessingFilterEntryPoint' defined in ServletContext resource [/WEB-INF/context/security.xml]; nested exception is java.lang.ClassNotFoundException: org.springframework.security.ui.cas.CasProcessingFilterEntryPoint
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1141)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1105)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:386)
at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
at org.springframework.security.config.EntryPointInjectionBeanPostProcessor.postProcessBeforeInitialization(EntryPointInjectionBeanPostProcessor.java:37)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:350)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1331)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
... 65 more
Caused by: java.lang.ClassNotFoundException: org.springframework.security.ui.cas.CasProcessingFilterEntryPoint
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1387)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1233)
at org.springframework.util.ClassUtils.forName(ClassUtils.java:211)
at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:385)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1138)
... 78 more

Do you know how I can have the class org.springframework.security.ui.cas ?

Thanks

Matt Fleming's picture

It's found in spring-security-cas-client jar

I don't know how you do dependencies in your project, but here's how you do it in maven:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-cas-client</artifactId>
    <version>2.0.4</version>
</dependency>

-Matt

What is UserDetailsService?

What is UserDetailsService? Which value must we put in security.xml?

Matt Fleming's picture

Roll your own or pick an existing one.

The one listed in the xml file is a custom bean. There are plenty of existing implementations though. Check out the javadoc

-Matt

I have this error when I log

I have this error when I log in the website :

java.lang.ClassCastException: org.kablink.teaming.asmodule.spring.security.config.TeamingAuthenticationManager cannot be cast to org.kablink.teaming.security.authentication.AuthenticationManager

Do you know how I can fix it?

Thanks
Laura

Matt Fleming's picture

Not really

Those sound like completely custom implementations. Does TeamingAuthenticationManager extend/implement AuthenticationManager?

-Matt

Spring security , CAS authentication and database authorization

Hi Matt,

I have tried to follow this configuration but my custom UserDetailsService is never called. on adding more break points in org.springframework.security.cas.authentication.CasAuthenticationProvider it seems that this is never called I have been struggling for a fews days if you could show me some direction that would be much appreciated. I have all the code and configuration details here...

http://stackoverflow.com/questions/13240684/spring-security-with-cas-authentication-and-custom-authorization/13274694#13274694

Thanks in advance

Matt Fleming's picture

Here's the concise security.xml configuration

Here's the concise version of the security configuration. This isn't something that you would be able to totally copy and paste of course. At a minimum the ROLEs are going to be different, as well as the service and userDetailsService definition.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="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-2.0.xsd
                                                http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
 
    <sec:http lowercase-comparisons="false" entry-point-ref="casProcessingFilterEntryPoint">
        <sec:intercept-url pattern="/images/*" filters="none"/>
        <sec:intercept-url pattern="/styles/*" filters="none"/>
        <sec:intercept-url pattern="/scripts/*" filters="none"/>
        <sec:intercept-url pattern="/403.jsp" filters="none"/>
        <sec:intercept-url pattern="/404.jsp" filters="none"/>
        <sec:intercept-url pattern="/error.jsp" filters="none"/>
        <sec:intercept-url pattern="/**/*.html*"
                           access="ROLE_ALLOWED_ROLES_GO_HERE"
                           requires-channel="https"/>
        <sec:logout logout-success-url="https://localhost:5543/cas/logout" invalidate-session="false"/>
        <sec:port-mappings>
            <sec:port-mapping http="5580" https="5543"/>
            <sec:port-mapping http="80" https="443"/>
        </sec:port-mappings>
    </sec:http>
 
    <bean id="passwordEncoder" class="org.springframework.security.providers.encoding.ShaPasswordEncoder"/>
 
    <!-- where do I go when I need authentication -->
    <bean id="casProcessingFilterEntryPoint" class="org.springframework.security.ui.cas.CasProcessingFilterEntryPoint">
        <property name="loginUrl" value="https://localhost:5543/cas/login"/>
        <property name="serviceProperties" ref="serviceProperties"/>
    </bean>
 
    <!-- which service (application) am I authenticating -->
    <bean id="serviceProperties" class="org.springframework.security.ui.cas.ServiceProperties">
        <property name="service" value="https://localhost:5543/rtos/j_spring_cas_security_check"/>
        <property name="sendRenew" value="false"/>
    </bean>
 
    <sec:authentication-manager alias="authenticationManager"/>
 
    <bean id="casSingleSignOutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter">
        <sec:custom-filter before="CAS_PROCESSING_FILTER"/>
    </bean>
 
    <bean id="casProcessingFilter" class="org.springframework.security.ui.cas.CasProcessingFilter">
        <sec:custom-filter after="CAS_PROCESSING_FILTER"/>
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationFailureUrl" value="/403.jsp"/>
        <property name="defaultTargetUrl" value="/"/>       
    </bean>
 
    <bean id="casAuthenticationProvider" class="org.springframework.security.providers.cas.CasAuthenticationProvider">
        <sec:custom-authentication-provider/>
        <property name="userDetailsService" ref="pickAnImplementationOfUserDetailsServiceOrRollYourOwn"/>
        <property name="serviceProperties" ref="serviceProperties"/>
        <property name="ticketValidator">
            <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <constructor-arg index="0" value="https://localhost:5543/cas"/>
            </bean>
        </property>
        <property name="key" value="my_password_for_this_auth_provider_only"/>
    </bean>
 
    <!-- Log failed authentication attempts to commons-logging -->
    <bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener"/>
 
</beans>

-Matt

Why this (security.xml) is different from securityContext.xml?

I download CAS 3.3.5 and they have applicationcontext.xml and securityContext.xml. The securityContext.xml has different name and format
from your security.xml. Why? and how Spring Security knows named "security.xml" or "securityContext.xml"? where is defined?

Thank you

Matt Fleming's picture

In the actual CAS application?

In the actual CAS application, they have some kind of manager application. As far as I can tell, it really isn't necessary at all to enable. In general, "how spring knows which xml files to load" is based upon how the spring container is instantiated. In most web apps, people use a servlet filter to instantiate the container on each request.. so you would see configuration in the web.xml. The question isn't specific to spring security though.. it's really a spring in general question. You should really read the documentation on how to instantiate a spring container; it is well written and explains everything.

-Matt

CAS Session Time out

Hi,
This is a very useful blob that you shared.
I am facing the issue in CAS during session expire.
Added the below in the web.xml (2 min session timeout)

<session-config>
  <session-timeout>2</session-timeout>
</session-config> 

After session expire, and browing any page from UI, request goes to Method loadUserByUsername(...).

I wanted to display popup where user again enter his/her login credential and continue to work.

I have alreay mentioned the url in but not working.

Please let me know, how to achive this.

Thanks in advance.

-Bill

Matt Fleming's picture

What about your CAS timeout?

I'm not sure how things are deployed in your system. My guess is that the reason you're not seeing the login prompt is because your CAS authentication is still valid (meaning you have a browser session cookie with a valid TGT). So from the sequence diagram, #1-5 is happening like normal, #6-9 is NOT happening because you don't need a login because CAS doesn't need to create a new TGT, #10-16 then happens. The user might notice a bunch of redirects but the end result is that they didn't have to login again.

If you think about it, you're essentially testing the single sign-on flow with CAS. If your CAS application is defined in the same web.xml as your application, then I don't know how this is possible but most of the time you won't be doing this. If you really want the users to be logged out after two minutes of inactivity, you should be calling your application's logout upon session expiration. Check out the logoutFilter bean to set that up (if you don't know how that works). If you have single sign-out implemented CAS will send a sign-out request to all authenticated applications.

Hope this helps..
-Matt

CAS Session Time out

Thanks Matt !!!

I am using the CAS server and didn't changed anything in CAS configuration file.
I am using default CAS, web.xml where session time out defined 5 min.

I have also tested application after 15 min, but not page is displyaing whatever mentioned in /page/relogin.jsp

FYI, I am using Spring mappiing for CAS and relogin.jsp resides in my application and CAS is deployed separately.
From application sending request to CAS server for authentication.

Please guide me to resolve the issue.
FYI, Previously I have used Acegi and it was working fine. I wanted to implement same using CAS.

Thanks,
-Bill

Matt Fleming's picture

Server side session is not relevant for CAS login

The user's session timeout for CAS isn't really relevant. The key thing is whether or not the ticket granting ticket is still present. The TGT is created in step 9 and is stored as a browser session cookie. Once the browser has a TGT steps #6-9 will not occur. So the key is that you want to get rid of the TGT.

Ticket-granting cookies (including the TGT) will be destroyed when the client closes the browser or when the clients clicks on the Logout link.. You call logout by having the browser call /j_spring_security_logout. Once the browser hits that URL, spring will use the logoutFilter and do the redirect to /logout that you want (which will kill the TGT and cookie).

So the key here is to make your application call logout. People do this in a variety of ways but since you are using CAS, you'll need to actually have the browser call logout. You won't be able to do this just on the server side. This is because the browser itself is the link between CAS and your web application (via the browser session level cookies).

-Matt

Session timeout

Thanks Matt!!!

As suggested by you, I need to call logout. But at the time of session time out, I don't want to logout from application. At the time of session time out, I wanted to display one pop up where user again enter his/her login credential and continue there work without login from the start and again open all the tabs used in the application.

Thanks

The logoutFilter bean

The logoutFilter bean definition is (in spring configuration)

<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
  <constructor-arg value="https://localhost:7002/cas/logout?url=https://localhost:7001/Admin/"/> 
  <constructor-arg>
	<list>
		<bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
	</list>
   </constructor-arg>
</bean>

pb with channelprocessingfilter

Quick Question

In your example is your CAS server on the same server as your application? I am having trouble with /j_spring_cas_security_check that is the returned service of the CAS server.

---more info---
Basically I go to my secured resource on computer B, it redirects me to my CAS server on computer A. I login using the JDBC authenticator, it succeeds and redirects me back to computer B (/j_spring_cas_security_check). This is where I get issues.

The browser states The requested resource (/COMPB/j_spring_cas_security_check) is not available. The logs from COMPB is complaining about no Authentication Object found in the SecurityContext...

INFO: Server startup in 4610 ms
[DEBUG org.springframework.security.util.FilterChainProxy:194 ] Converted URL to lowercase, from: '/secured/home.htm'; to: '/secured/home.htm'
[DEBUG org.springframework.security.util.FilterChainProxy:201 ] Candidate is: '/secured/home.htm'; pattern is /secured/*; matched=true
[DEBUG org.springframework.security.util.FilterChainProxy:366 ] /secured/home.htm at position 1 of 6 in additional filter chain; firing Filter: 'org.springframework.security.context.HttpSessionContextIntegrationFilter[ order=200; ]'
<b>[DEBUG org.springframework.security.context.HttpSessionContextIntegrationFilter:286 ] HttpSession returned null object for SPRING_SECURITY_CONTEXT</b>
[DEBUG org.springframework.security.context.HttpSessionContextIntegrationFilter:209 ] New SecurityContext instance will be associated with SecurityContextHolder
[DEBUG org.springframework.security.util.FilterChainProxy:366 ] /secured/home.htm at position 2 of 6 in additional filter chain; firing Filter: 'org.jasig.cas.client.session.SingleSignOutFilter@99176f'
[DEBUG org.springframework.security.util.FilterChainProxy:366 ] /secured/home.htm at position 3 of 6 in additional filter chain; firing Filter: 'org.springframework.security.ui.cas.CasProcessingFilter[ order=600; ]'
[DEBUG org.springframework.security.util.FilterChainProxy:366 ] /secured/home.htm at position 4 of 6 in additional filter chain; firing Filter: 'org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter[ order=1100; ]'
[DEBUG org.springframework.security.ui.savedrequest.SavedRequest:314 ] pathInfo: both null (property equals)
[DEBUG org.springframework.security.ui.savedrequest.SavedRequest:314 ] queryString: both null (property equals)
[DEBUG org.springframework.security.ui.savedrequest.SavedRequest:330 ] requestURI: arg1=/appB/secured/home.htm; arg2=/appB/secured/home.htm (property equals)
[DEBUG org.springframework.security.ui.savedrequest.SavedRequest:330 ] serverPort: arg1=8080; arg2=8080 (property equals)
[DEBUG org.springframework.security.ui.savedrequest.SavedRequest:330 ] requestURL: arg1=http://compB:8080/appB/secured/home.htm; arg2=http://compB:8080/appB/secured/home.htm (property equals)
[DEBUG org.springframework.security.ui.savedrequest.SavedRequest:330 ] scheme: arg1=http; arg2=http (property equals)
[DEBUG org.springframework.security.ui.savedrequest.SavedRequest:330 ] serverName: arg1=compB; arg2=compB (property equals)
[DEBUG org.springframework.security.ui.savedrequest.SavedRequest:330 ] contextPath: arg1=/appB; arg2=/appB(property equals)
[DEBUG org.springframework.security.ui.savedrequest.SavedRequest:330 ] servletPath: arg1=/secured/home.htm; arg2=/secured/home.htm (property equals)
[DEBUG org.springframework.security.wrapper.SavedRequestAwareWrapper:100 ] Wrapper replaced; SavedRequest was: SavedRequest<a href="http://compB:8080/appB/secured/home.htm" title="//compB:8080/appB/secured/home.htm" target="_blank" rel="nofollow">http://compB:8080/appB/secured/home.htm</a>
[DEBUG org.springframework.security.util.FilterChainProxy:366 ] /secured/home.htm at position 5 of 6 in additional filter chain; firing Filter: 'org.springframework.security.ui.ExceptionTranslationFilter[ order=1400; ]'
[DEBUG org.springframework.security.util.FilterChainProxy:366 ] /secured/home.htm at position 6 of 6 in additional filter chain; firing Filter: 'org.springframework.security.intercept.web.FilterSecurityInterceptor@c0890f'
[DEBUG org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource:224 ] Candidate is: '/secured/home.htm'; pattern is /**; matched=true
[DEBUG org.springframework.security.intercept.AbstractSecurityInterceptor:250 ] Secure object: FilterInvocation: URL: /secured/home.htm; ConfigAttributes: [ROLE_ANONYMOUS]
[DEBUG org.springframework.web.context.support.XmlWebApplicationContext:273 ] Publishing event in context [org.springframework.web.context.support.XmlWebApplicationContext@16a38b5]: org.springframework.security.event.authorization.AuthenticationCredentialsNotFoundEvent[source=FilterInvocation: URL: /secured/home.htm]
[DEBUG org.springframework.security.ui.ExceptionTranslationFilter:150 ] Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
	at org.springframework.security.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:342)
	at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:254)
	at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:106)
	at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:371)
	at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:371)
	at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:371)
	at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:271)
	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:371)
	at org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:99)
	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:371)
	at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
	at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:371)
	at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:174)
	at org.springframework.security.util.FilterToBeanProxy.doFilter(FilterToBeanProxy.java:99)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:172)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:174)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:875)
	at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
	at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
	at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
	at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:689)
	at java.lang.Thread.run(Unknown Source)
[DEBUG org.springframework.security.ui.ExceptionTranslationFilter:200 ] Authentication entry point being called; SavedRequest added to Session: SavedRequest<a href="http://compb:8080/appb/secured/home.htm" title="//compb:8080/appb/secured/home.htm" target="_blank" rel="nofollow">http://compb:8080/appb/secured/home.htm</a>
[DEBUG org.springframework.security.context.HttpSessionContextIntegrationFilter:255 ] SecurityContextHolder now cleared, as request processing completed
[DEBUG org.springframework.security.util.FilterChainProxy:194 ] Converted URL to lowercase, from: '/j_spring_cas_security_check?ticket=st-1-hoycdb4yskn4nibsmbea-cas'; to: '/j_spring_cas_security_check?ticket=st-1-hoycdb4yskn4nibsmbea-cas'
[DEBUG org.springframework.security.util.FilterChainProxy:201 ] Candidate is: '/j_spring_cas_security_check?ticket=st-1-hoycdb4yskn4nibsmbea-cas'; pattern is /secured/*; matched=false
[DEBUG org.springframework.security.util.FilterChainProxy:164 ]  has an empty filter list

Clarification on configuring Spring Security CAS

I'm struggling to understanding just how Spring Security and CAS interact and thus how they should be configured. I'd appreciate it if you could answer the following questions for me --

  1. If I were to add the following to the applicationContext of my application (a Spring Security CAS client), then only that application would allow for single-sign-out, correct?
    <bean id="casSingleSignOutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter">
           <sec:custom-filter before="CAS_PROCESSING_FILTER"/>
    </bean>
    And thus to allow each of my applications (all the other Spring Security CAS clients) to offer single-sign-out I'd have to add the above to their respective applicationContexts as well, correct?

  2. If I wanted CAS to send attributes with user validation I'd have to add the following to the applicationContext of an application -- i.e. a Spring Security CAS Client -- correct? And only the application with the following in it's applicationContext could provide the attributes with the user validation, correct?
    <bean id="casAuthenticationProvider" class="org.springframework.security.providers.cas.CasAuthenticationProvider">
            <property name="userDetailsService" ref="myUserDetailsServiceImpl"/>
            <property name="serviceProperties" ref="serviceProperties"/>
            <property name="ticketValidator">
                <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                    <constructor-arg index="0" value="https://localhost:5543/cas"/>
                </bean>
            </property>
        </bean>

Thanks.

Matt Fleming's picture

1) Single sign-out: The CAS

1) Single sign-out: The CAS server will send a single sign out request to all of the applications that the user signed into. The filter is what processes the sign out request for each application. If an application doesn't process the sign out request from the CAS server, it would not participate in single sign out. By that I mean let's say you have three applications A, B and C. You have signed on to A, then bounced over to B and C. The single sign on would happen when you bounced to B and C. Now let's say you're in C and you do a sign out (which triggers the CAS logout). CAS will send a single sign out request to all three applications. If application B were not configured to have the listener, the user wouldn't be signed out of application B. The user could open up application B and things would still be good (no login required), application A (which had a listener) would require another login.

2) The question is ambiguous to me (kind of like the last one). Which step in the collaboration diagram are you talking about? Why do you need to do this?

-Matt

Single sign-out & principal attributes

1) Thanks for the explanation/example -- it clarified my understanding of the relationship between CAS & Spring Security. I was originally under the impression that once integrated with CAS, Spring was completely absolved of all security responsibilities and details. This misunderstanding lead to my difficulties in understanding how to configure Spring Security & CAS.

2) Sorry for the ambiguity of the question. The crux of my question is: can principal attributes be accessed from CAS rather than each individually secured app? For example, in Spring Security one can get principal details from the security context; how can one get principal details from CAS? If I understood the CAS wiki correctly, in step #8 (according to your collaboration diagram) the validation ticket is (or can be) populated with principal details.

Matt Fleming's picture

Re: principal attributes

By default the details about the principal other than the login name are the responsibility of the application authenticating with CAS. CAS does authentication not authorization. Maybe you knew this already and you're trying to stuff some authorization level stuff in a response somewhere? I'm sure it's possible to change the response coming from the validate call from CAS. You'll probably have to change a bunch of stuff in CAS and the spring security CAS client layer though (because of the parsing and storage of the response). I think the response XML from a validation call is the actual user's ID not, anything else. The easiest way to know what is coming back in each call is to debug a hooked up application and set the breakpoints in the spring cas client layer.

Like you said in the #1, spring is responsible for loading the security context based upon the user id (credentials) that comes back from CAS. Spring is also responsible for redirecting to CAS when the security context hasn't been populated yet.

Here's a discussion on how the ruby version of the CAS server allows service_validate/proxy_validate responses to include arbitrary data. If you wanted to do this for the java server, you'd probably be breaking the CAS api if you will, in that the xml wouldn't technically validate against the cas response schema.

-Matt

No bean named 'springSecurityFilterChain' is defined

I'm trying to integrate CAS into Spring Security, however, every time I run my app I get the error: No bean named 'springSecurityFilterChain' is defined

If I revert to only Spring Security based form login, my app works just fine; therefore, I believe the error is caused by the xml markup pertaining to CAS integration, but I don't see anything wrong in the xml. I've compared my xml file to what you posted, and my xml file seems to possess all the requirements. Below is my xml file, I'd appreciate it if you could review it to see if you can find the cause of the error. Thanks.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:sec="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/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security-3.0.xsd">
 
    <context:annotation-config/>
    <context:component-scan base-package="com.facility.web.security"/>
 
    <bean id="casSingleLogoutFilter" class="org.jasig.cas.client.session.SingleLogoutFilter"/>
 
    <bean id="casTicketValidator" class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
        <constructor-arg value="http://localhost:8080/facility-cas/"/>
    </bean>
 
    <bean id="casService" class="org.springframework.security.cas.ServiceProperties">
        <property name="service" value="http://localhost:9999/facilty-web/sign-in"/>
    </bean>
 
    <bean id="casAuthEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
        <property name="loginUrl" value="http://localhost:8080/facility-cas"/>
        <property name="serviceProperties" ref="casService"/>
    </bean>
 
    <bean id="authenticationUserDetailsService" class="org.spring.framework.security.core.userdetails.UserDetailsByNameServiceWrapper">
        <property name="userDetailsService" ref="facilityUserDetailsService"/>
    </bean>
 
    <bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
        <property name="ticketValidator" ref="casTicketValidator"/>
        <property name="serverProperties" ref="casService"/>
        <property name="key" value="facility-cas"/>
        <property name="authenticationUserDetailsService" ref="authenticationUserDetailsService"/>
    </bean>
 
    <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider ref="casAuthenticationProvider"/>
    </sec:authentication-manager>
 
    <bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="filterProcessesUrl" value="/sign-in"/>
    </bean>
 
    <sec:http auto-config="true" entry-point-ref="casAuthEntryPoint">
        <sec:custom-filter ref="casAuthenticationFilter" position="CAS_FILTER"/>
        <sec:intercept-url pattern="/**" access="ROLE_USER, ROLE_SUPER_USER, ROLE_ADMIN"/>
    </sec:http>

Matt Fleming's picture

web.xml

Do you have the filter setup in web.xml?

<!-- Enables Spring Security -->
    <filter>
        <filter-name>securityFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
 
    <filter-mapping>
        <filter-name>securityFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

If so, the delegating filter proxy will use a bean named the same as the filter name. In the example above the FilterChainProxy class must be called securityFilter. In my xml, I have a definition for the securityFilter

    <bean id="securityFilter" class="org.springframework.security.util.FilterChainProxy">
        <sec:filter-chain-map path-type="ant">
            <sec:filter-chain pattern="/**"
                              filters="channelProcessingFilter,httpSessionContextIntegrationFilter,logoutFilter,casSingleSignOutFilter,rememberMeProcessingFilter,anonymousUserProcessingFilter,casProcessingFilter,exceptionTranslationFilter,securityContextHolderAwareRequestFilter,filterInvocationInterceptor"/>
        </sec:filter-chain-map>
    </bean>

I don't use the compact version of the spring-security.xml so that I can define everything myself. It looks like your problem is that the default springSecurityFilterChain bean isn't being created. My first guess would be to make sure that everything is hooked up properly in the web.xml file. How are you loading your spring container configuration files? e.g

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath*:spring-annotation.xml
            classpath*:spring-hibernate.xml
            classpath*:spring-resources.xml
            classpath*:spring-security.xml
            classpath*:spring-queries.xml
        </param-value

If spring-security.xml file is there then the bean wouldn't get created. Next I'd put one change in at time (from the login version of the security file to the CAS version) to see which element is messing things up.

-Matt

web.xml

The filter is setup as follows in web.xml

<!-- Spring Security Filter Mapping -->
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

I'm loading the configuration files by:
<context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>
 ...
 classpath:applicationContext-security.xml
 ...
 </param-value>
<context-param>

Quote: If spring-security.xml file is there then the bean wouldn't get created.

Did you mean: if spring-security.xml file is NOT there then the bean will not get created?

Matt Fleming's picture

Typo

Right. If it's not there then the bean will not get created. I think your problem may be with the:<sec:custom-filter ref="casAuthenticationFilter" position="CAS_FILTER"/> declaration. I don't put that line in the <sec:http/> section. Try putting that into the bean definition for the casAuthenticationFilter instead. Also you should turn on debugging and see if any errorish messages show up.

-Matt

(401) No principal was found in the response from the CAS server

The Spring context error was caused by:

<bean id="authenticationUserDetailsService" class="org.spring.framework.security.core.userdetails.UserDetailsByNameServiceWrapper">
     <property name="userDetailsService" ref="facilityUserDetailsService"/>
</bean>
I commented out the above and added the following property to casAuthenticationProvider:
<property name="userDetailsService" ref="faciltyUserDetailsService"/>

Any idea what is wrong with the authenticationUserDetailsService declaration?

While my app now runs, whenever I try to authenticate via CAS, I always get HTTP Status 401 - Authentication Failed: No principal was found in the response from the CAS server. Oddly this happens only when I go to my apps' url, for example http://localhost:9999/my-webapp/sign-in

However, if I go to the CAS login url, http://localhost:8080/cas/login, and enter user credentails CAS authenticates and then redirects to the default successful login page.

Do you think the 401 error is caused by me commenting out authenticationUserDetailsService from the security.xml file?

Matt Fleming's picture

Never used the wrapper

I've never used the wrapper around the userDetailsService before. My userDetailsService is just a normal class that implements the interface. I also don't know why you're getting the 401. I'd enable verbose logging and probably step through the CAS client-side (spring security) code to see exactly what is coming back from your CAS server.

-Matt

CAS with Spring-Security integration with BPM Directory for SSO

Hi,

Need ur help and guidance.

Above u have used a DB / internal Map to get the user details.

But please guide me in finding the solution where i need client Website to be authenticated using spring-security with CAS and the user credentials are in Oracle bpm directory. Trouble is neither spring security nor oracle bpm directory give access to password information.

Also i tried all possible configurations in vain for the CAS to use CustomUserServiceDetail.

In above scenario how to get a work around for the user authentication.

Attached is the applicationContext-security.xml which i use.

<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:sec="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-2.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
 
    <sec:http entry-point-ref="casProcessingFilterEntryPoint">
        <sec:intercept-url pattern="/secur/extreme/**" access="ROLE_SUPERVISOR" requires-channel="https"/>
        <sec:intercept-url pattern="/secur/**" access="ROLE_USER" />           
        <sec:logout logout-success-url="/cas-logout.jsp"/>
    </sec:http>
    <sec:authentication-manager alias="authenticationManager"/>
 
    <bean id="casProcessingFilter" class="org.springframework.security.ui.cas.CasProcessingFilter">
        <sec:custom-filter after="CAS_PROCESSING_FILTER"/>
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationFailureUrl" value="/casfailed.jsp"/>
        <property name="defaultTargetUrl" value="/"/>
        <property name="proxyGrantingTicketStorage" ref="proxyGrantingTicketStorage" />
        <property name="proxyReceptorUrl" value="/secur/receptor" />
    </bean>
 
    <bean id="casProcessingFilterEntryPoint" class="org.springframework.security.ui.cas.CasProcessingFilterEntryPoint">
        <property name="loginUrl" value="https://172.24.11.90:7002/cas/login"/>
        <property name="serviceProperties" ref="serviceProperties"/>
    </bean>
	<bean id="serviceProperties" class="org.springframework.security.ui.cas.ServiceProperties">
        <property name="service" value="http://172.24.11.90:8201/home.jsp"/>
        <property name="sendRenew" value="false"/>
    </bean>
 
    <bean id="casAuthenticationProvider" class="org.springframework.security.providers.cas.CasAuthenticationProvider">
        <sec:custom-authentication-provider/>
        <property name="userDetailsService" ref="customUserDetailsService">	  
		 </property>
        <property name="serviceProperties" ref="serviceProperties" />
        <property name="ticketValidator">
        	<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
        		<constructor-arg index="0" value="https://172.24.11.90:7002/cas" />
        		<property name="proxyGrantingTicketStorage" ref="proxyGrantingTicketStorage" />
        		<property name="proxyCallbackUrl" value="https://172.24.10.70:7002/cas-sample/secur/receptor" />
            </bean>
        </property>
        <property name="key" value="an_id_for_this_auth_provider_only"/>
    </bean>
      <bean id="customUserDetailsService" class="spring.secure.login.CustomUserDetailsService">
		    </bean>
 
    <bean id="casSingleSignOutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter">
        <sec:custom-filter before="CAS_PROCESSING_FILTER"/>
    </bean>
 
    <bean id="proxyGrantingTicketStorage" class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl" />
 
</beans>

With Regards,
Harsha

Matt Fleming's picture

Just roll your own class...

You'll probably need to roll your own class to handle this. Just implement UserDetailsService and you're all set. I'm not familiar with oracle bpm as I haven't had to work with it yet and am not sure how it applies to storing user information. I'm not really sure why you would need the user's password to load their details from BPM though. If you absolutely need the a user's password to access bpm, I'd probably try to use an administrator's userid/pw to access the logged in user's details then. So kinda like we do with pooled db access where it is a single userid/pw to get other information. Sorry I can't be of more help.

-Matt

Implemented UserDetailService Class yet CASserver not validating

Hi,

As suggested, i had implemented UserDetailservice class[CustomUserDetailsService]. CAS server is not validating Username/password with the userDetails object but allows login of Users whose Username and Password are the same everytime. So please suggest if any changes to be done in applicationContext-security.xml file so that the CAS server actually validates with data present in UserDetailsobject returned from loadUserByUsername(String userName) method call in implemented UserDetailservice class.

Also as you suggested using admin credentials to get connected to BPM directory, we will get the required user details using the Username. But BPM directory does not give access to password information. Hence the UserDetails object returned from UserDetailservice class would not have password information which would be required for CAS server to authenticate.
So could you please suggest a away to authenticate in this case.

With Regards,
Harsha

Matt Fleming's picture

Confusion

There seems to be some confusion here. CAS doesn't need the UserDetailsService, your application does. CAS is responsible for authentication only, your application is responsible for authorization (hence the granted authority piece of UserDetailsService). It sounds like you're on your way for the UserDetailsService (your application). For CAS, you should read the documentation about configuring Authentication. You may have to write your own mechanism and deploy it with the CAS application to hook to bpm.

-Matt

UserName value getting as "_cas_stateful_"

Hi,

As per your advice, had written a custom class to do authentication from CAS server. But for authorisation the username got inside Customised UserDetailsService is "_cas_stateful_". Any specific filter to be used for the same. Pasted is the casProcessingFilter being used.

<bean id="casProcessingFilter" class="org.springframework.security.ui.cas.CasProcessingFilter">
		<sec:custom-filter after="CAS_PROCESSING_FILTER" />
		<property name="authenticationManager" ref="authenticationManager" />
		<property name="authenticationFailureUrl" value="/casfailed.jsp" />
		<property name="defaultTargetUrl" value="/" />
	</bean>
 
<bean id="casAuthenticationProvider"
		class="org.springframework.security.providers.cas.CasAuthenticationProvider">
		<!-- <sec:custom-authentication-provider/>  -->
		<property name="userDetailsService" ref="customUserDetailsServiceImpl"/>
		<property name="serviceProperties" ref="serviceProperties" />
		<property name="ticketValidator">
			<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
				<constructor-arg index="0"
					value="https://localhost:7002/cas" />
			</bean>
		</property>
		<property name="key" value="an_id_for_this_auth_provider_only" />
	</bean>

With Regards,
Harsha