Announcement Announcement Module
Collapse
No announcement yet.
FilterChain: ExceptionTranslationFilter cannot detect AuthenticationException (CAS) Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • FilterChain: ExceptionTranslationFilter cannot detect AuthenticationException (CAS)

    Hi all,

    I am currently trying to authenticate with acegi against a CAS server and therefore I established a local test environment. Using a CAS filter with filter class edu.yale.its.tp.cas.client.filter.CASFilter in my webapp works (no acegi is involved). My browser got redirected to CAS' default login screen and after entering valid user/password it got redirected to the protected resource and the TGC-cookie has been created.

    BTW - environment is:

    - CAS-Server 3.0.7 (using Acegi InMemoryDaoImpl for processing authentication)
    - CAS Client 2.1.1
    - Tomcat 5.5.20
    - Acegi 1.0.3

    Adding acegi fails namely because the ExceptionTranslationFilter doesn't get the chance to detect the AuthenticationException that is thrown by the CasAuthenticationProvider when an unauthenticated client tries to browse a protected resource.
    Since debug logging level throws out way too many messages and info level hardly any I replaced the main classes involved (CasProcessingFilter, CasAuthenticationProvider, ExceptionTranslationFilter) with extending classes and added some output in order to check what is going wrong.
    What happens here is:

    1. CasProcessingFilter.doFilter is called (right, since it is the first in the chain)
    2. CasProcessingFilter checks if authentication is required (according to the url defined with property filterProcessesUrl) - if so it passes control on to the defined AuthenticationManager
    3. The AuthenticationManager checks the received UsernamePasswordAuthenticationToken and detects the BadCredentials - a BadCredentialsException is thrown - so far, everthing is just fine
    4. But then - instead of proceeding with the filter chain so the ExceptionTranslationFilter can detect and process the Exception (by redirecting to the authenticationEntryPoint) - the Exception got caught within the CasProcessingFilter, respectively its SuperClass AbstractProcessingFilter which handles it in its method unsuccessfulAuthentication and then redirects to the authenticationFailureUrl (that is if I got it right from the source code).

    Am I missing some basic unterstanding in how that filter chain stuff works and what parts did I misconfigure?
    I as well tried it with the configuration Acegi provides at sourceforge beneath acegisecurity/samples/contacts/src/main/webapp/cas/WEB-INF/ and I got the same result. However, the redirection works when I enforce the invocation of ExceptionTranslationFilter.handleException in ExceptionTranslationFilterTest.doFilter - but this shouldn't be the way to go.

    Best regards,
    Sebastian

    web.xml currently in use:
    HTML Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'>
    
    <!--
      - Contacts web application
      -
      - web.xml for "cas" artifact only.
      -
      - $Id$
      -->
    
    <web-app>
    
        <display-name>Security Module</display-name>
    
    	<!--
    	  - Location of the XML file that defines the root application context
    	  - Applied by ContextLoaderListener.
    	  -->
    	<context-param>
    	    <param-name>contextConfigLocation</param-name>
    	    <param-value>
    	    	classpath:/META-INF/applicationContext.xml
    	    </param-value>
    	</context-param>
    
    	<context-param>
    		<param-name>log4jConfigLocation</param-name>
    		<param-value>/WEB-INF/classes/log4j.properties</param-value>
    	</context-param>
    
    	<!-- Required for CAS ProxyTicketReceptor servlet. This is the
    	     URL to CAS' "proxy" actuator, where a PGT and TargetService can
    	     be presented to obtain a new proxy ticket. THIS CAN BE
    	     REMOVED IF THE APPLICATION DOESN'T NEED TO ACT AS A PROXY -->
        <context-param>
            <param-name>edu.yale.its.tp.cas.proxyUrl</param-name>
            <param-value>http://localhost/cas/proxy</param-value>
        </context-param>
    
        <filter>
            <filter-name>Acegi Filter Chain Proxy</filter-name>
            <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
            <init-param>
                <param-name>targetClass</param-name>
                <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
            </init-param>
        </filter>
    
        <filter-mapping>
          <filter-name>Acegi Filter Chain Proxy</filter-name>
          <url-pattern>/*</url-pattern>
        </filter-mapping>
    
    	<!--
    	  - Loads the root application context of this web app at startup.
    	  - The application context is then available via
    	  - WebApplicationContextUtils.getWebApplicationContext(servletContext).
        -->
    	<listener>
    		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    	</listener>
    
        <listener>
    		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    	</listener>
    
    
    
     	<welcome-file-list>
    		<welcome-file>index.jsp</welcome-file>
    	</welcome-file-list>
    
      	<taglib>
          <taglib-uri>/spring</taglib-uri>
          <taglib-location>/WEB-INF/spring.tld</taglib-location>
      	</taglib>
    
    </web-app>
    applicationContext.xml currently in use:
    HTML Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    
    <beans>
    
    <bean id="serviceProperties" class="org.acegisecurity.ui.cas.ServiceProperties">
    	<property name="service">
    		<value>https://localhost/securitymodule/</value>
    	</property>
    	<property name="sendRenew"><value>false</value></property>
    </bean>
    
    <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
        <property name="filterInvocationDefinitionSource">
            <value>
    		    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
    		    PATTERN_TYPE_APACHE_ANT
                /**=casProcessingFilter,exceptionTranslationFilter
            </value>
        </property>
    </bean>
    
    <!-- <bean id="casProcessingFilter" class="org.acegisecurity.ui.cas.CasProcessingFilter"> -->
    <bean id="casProcessingFilter" class="net.quintessence.test.CasProcessingFilterTest">
    	<property name="authenticationManager"><ref bean="authenticationManager"/></property>
    	<property name="authenticationFailureUrl"><value>/casfailed.jsp</value></property>
    	<property name="defaultTargetUrl"><value>/</value></property>
    	<property name="filterProcessesUrl"><value>/</value></property>
    </bean>
    
    <!-- <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> -->
    <bean id="exceptionTranslationFilter" class="net.quintessence.test.ExceptionTranslationFilterTest">
    	<property name="authenticationEntryPoint"><ref local="casProcessingFilterEntryPoint"/></property>
    </bean>
    
    <bean id="casProcessingFilterEntryPoint" class="org.acegisecurity.ui.cas.CasProcessingFilterEntryPoint">
    	<property name="loginUrl"><value>https://localhost/cas/login</value></property>
    	<property name="serviceProperties"><ref bean="serviceProperties"/></property>
    </bean>
    
    <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
    	<property name="providers"><list><ref bean="casAuthenticationProvider"/></list></property>
    </bean>
    
    <!-- <bean id="casAuthenticationProvider" class="org.acegisecurity.providers.cas.CasAuthenticationProvider"> -->
    <bean id="casAuthenticationProvider" class="net.quintessence.test.CasAuthenticationProviderTest">
    	<property name="casAuthoritiesPopulator"><ref bean="casAuthoritiesPopulator"/></property>
    	<property name="casProxyDecider"><ref bean="casProxyDecider"/></property>
    	<property name="ticketValidator"><ref bean="casProxyTicketValidator"/></property>
    	<property name="statelessTicketCache"><ref bean="statelessTicketCache"/></property>
    	<property name="key"><value>my_password_for_this_auth_provider_only</value></property>
    </bean>
    
    <bean id="casProxyTicketValidator" class="org.acegisecurity.providers.cas.ticketvalidator.CasProxyTicketValidator">
    	<property name="casValidate"><value>https://localhost/cas/proxyValidate</value></property>
    	<property name="proxyCallbackUrl"><value>https://localhost/securitymodule/casProxy/receptor</value></property>
    	<property name="serviceProperties"><ref bean="serviceProperties"/></property>
    	<!-- <property name="trustStore"><value>/some/path/to/your/lib/security/cacerts</value></property> -->
    </bean>
    
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
    	<property name="configLocation"><value>classpath:/ehcache-failsafe.xml</value></property>
    </bean>
    
    <bean id="ticketCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
    	<property name="cacheManager"><ref local="cacheManager"/></property>
    	<property name="cacheName"><value>ticketCache</value></property>
    </bean>
    
    <bean id="statelessTicketCache" class="org.acegisecurity.providers.cas.cache.EhCacheBasedTicketCache">
    	<property name="cache"><ref local="ticketCacheBackend"/></property>
    </bean>
    
    <bean id="casAuthoritiesPopulator" class="org.acegisecurity.providers.cas.populator.DaoCasAuthoritiesPopulator">
    	<property name="userDetailsService"><ref bean="inMemoryDaoImpl"/></property>
    </bean>
    
    <bean id="inMemoryDaoImpl"
    		class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
    		<property name="userMap">
    			<value>
    				peter=maffay,ADMIN
                    udo=lindenberg,DEVELOPER
    			</value>
    		</property>
    	</bean>
    
    <bean id="casProxyDecider" class="org.acegisecurity.providers.cas.proxy.RejectProxyTickets"/>
    
    </beans>

  • #2
    I'd recommend you start with the form-based login example which uses AuthenticationProcessingFilter, get that working and understand the purpose of the each filter and how they interact before moving on to CAS. The CasProcessingFilter does a similar job but there is a lot more going on.

    If you look at the CAS samples or other posts in this forum you will see that the filter chain consist of substantially more than what you have here. The flow you describe is for a failed login, and ExceptionTranslationFilter should have nothing to do with this. It is supposed to catch exceptions from further down the stack which indicate that authentication is required. You are missing a FilterSecurityInterceptor which is normally responsible for such exceptions.

    The filterProcessesUrl property is a one-off Url, solely for authentication requests. In other examples, you will see it set to "/j_acegi_cas_security_check", as opposed to "/j_acegi_security_check" in the form login example. This would be the Url the CAS server redirects the user to *after* they have logged into CAS. You have it set to the the application root and the same as the defaultTargetUrl (which incidentally could easily result in an infinite loop between the two).

    So what is happening is that you are browsing to the root Url, the request is being submitted to the CAS provider and is failing because it doesn't contain a valid ticket.

    Read the manual section on "How CAS works", which lays out the sequence of events:

    http://acegisecurity.org/docbook/ace...s-how-it-works

    And don't disable the debug logging as it is your best chance (short of posting a working example) of getting your problems solved in this forum .
    Last edited by Luke Taylor; Jun 27th, 2007, 08:53 AM. Reason: typo

    Comment


    • #3
      Thanks for your advice, Luke - I moved back to the form based auth, added each filter deliberately and got it working. Adding CAS auth wasn't that hard with the form based login as a basis. I really should have done it like that in the first place....

      What I'd like CAS to do now is authentication without using a form (by using remote protocols like Hessian or SOAP for the authentication request). I recognized some configuration options in CAS (namely its web.xml, cas-servlet.xml and remoteServices.xml) which refer to remote services. I will try to get further information on that stuff - however, if anybody knows something about it, I'll be glad to hear about your experiences concerning CAS authentication without forms.

      Thanks again, Luke, regards,

      Sebastian

      Comment

      Working...
      X