title: Getting redirect loop with CAS Spring Security
body: <p>The following configuration is resulting in a redirect loop to a CAS server that otherwise works for other apps. I'm able to login and the resulting url is:</p>
  
  <pre><code>http://localhost:18080/ourapp/app/j_spring_cas_security_check?ticket=ST-18-CQMfGMeDPcXkdKGjosfj-localsso.subdomain.mycompany.com
  </code></pre>
  
  <p><strong>applicationContext-security.xml</strong></p>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  
  &lt;beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:p="http://www.springframework.org/schema/p"
  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.2.xsd"&gt;   
  
  &lt;!-- Enable security, let the casAuthenticationEntryPoint handle all intercepted 
  urls. The CAS_FILTER needs to be in the right position within the filter 
  chain. --&gt;
  &lt;security:http entry-point-ref="casEntryPoint" use-expressions="true" auto-config="false"&gt;
      &lt;!--&lt;security:intercept-url pattern="/**" access="permitAll"/&gt; --&gt;
        &lt;security:anonymous username="guest" granted-authority="isAnonymous()"/&gt;
      &lt;security:intercept-url pattern="/app/resources/**" access="hasAnyRole('ROLE_ANONYMOUS', 'cm_user')"/&gt;
      &lt;security:intercept-url pattern="/app/**" access="hasRole('cm_user')"/&gt;
      &lt;security:custom-filter position="CAS_FILTER" ref="casFilter"/&gt;
  
      &lt;security:session-management&gt;
          &lt;security:concurrency-control max-sessions="5" error-if-maximum-exceeded="true"/&gt;
     &lt;/security:session-management&gt;
  &lt;/security:http&gt;
  
  &lt;!-- 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. --&gt;
  &lt;security:authentication-manager alias="authenticationManager"&gt;
      &lt;security:authentication-provider ref="casAuthenticationProvider"/&gt;
  &lt;/security:authentication-manager&gt;
  
  &lt;!-- This section is used to configure CAS. The service is the actual redirect 
  that will be triggered after the CAS login sequence. --&gt;
  &lt;bean id="serviceProperties"
      class="org.springframework.security.cas.ServiceProperties"
      p:service="http://localhost:18080/ourapp/app/j_spring_cas_security_check"
      p:sendRenew="false"/&gt;
  
  &lt;!-- The CAS filter handles the redirect from the CAS server and starts 
  the ticket validation. --&gt;
  &lt;bean id="casFilter"
      class="org.springframework.security.cas.web.CasAuthenticationFilter"&gt;
  &lt;property name="authenticationManager" ref="authenticationManager"/&gt;
  &lt;/bean&gt;
  
  &lt;!-- The entryPoint intercepts all the CAS authentication requests. It redirects 
  to the CAS loginUrl for the CAS login page. --&gt;
  &lt;bean id="casEntryPoint"
      class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"
      p:serviceProperties-ref="serviceProperties"
      p:loginUrl="https://devmcauth01.nexus.mycompany.com:5443/login"/&gt;
  
  &lt;!-- Handles the CAS ticket processing. --&gt;
  &lt;bean id="casAuthenticationProvider"
    class="org.springframework.security.cas.authentication.CasAuthenticationProvider"&gt;
      &lt;property name="userDetailsService" ref="userDetailsService"/&gt;
      &lt;property name="serviceProperties" ref="serviceProperties" /&gt;
      &lt;property name="ticketValidator"&gt;
        &lt;bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator"&gt;
          &lt;constructor-arg index="0" value="https://devmcauth01.nexus.mycompany.com:5443" /&gt;
        &lt;/bean&gt;
      &lt;/property&gt;
      &lt;property name="key" value="cas"/&gt;
  &lt;/bean&gt;
  
  &lt;bean id="permissionConversionService"
      class="com.mycompany.connmgmt.misc.security.PermissionConversionServiceImpl"/&gt;
  
  &lt;bean id="userDetailsService"
      class="com.mycompany.connmgmt.misc.security.CmUserDetailsService"&gt;
      &lt;property name="userRepository" ref="userRepository"/&gt;
      &lt;property name="permissionConversionService" ref="permissionConversionService"/&gt;
  &lt;/bean&gt;
  
  &lt;bean id="accessDecisionManager" 
          class="org.springframework.security.access.vote.AffirmativeBased"&gt;
      &lt;property name="allowIfAllAbstainDecisions"&gt;
          &lt;value&gt;false&lt;/value&gt;
      &lt;/property&gt;
      &lt;property name="decisionVoters"&gt;
          &lt;list&gt;
             &lt;ref bean="roleVoter"/&gt;
          &lt;/list&gt;
      &lt;/property&gt;
  &lt;/bean&gt;
  
  &lt;bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"&gt;
      &lt;property name="rolePrefix" value=""/&gt;
  &lt;/bean&gt;
  
  &lt;bean id="loggerListener" 
      class="org.springframework.security.access.event.LoggerListener"/&gt;
  
  &lt;!-- Enable JSR250 annotations, disable Spring Security annotations for now --&gt;
  &lt;security:global-method-security secured-annotations="disabled" 
      jsr250-annotations="enabled" 
      access-decision-manager-ref="accessDecisionManager"/&gt;
  </code></pre>
  
  <p></p>
  
  <p><strong>web.xml</strong></p>
  
  <p>
  http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
      version="2.4"></p>
  
  <pre><code>&lt;display-name&gt;Our App&lt;/display-name&gt;
  
  &lt;filter&gt;
      &lt;filter-name&gt;OpenPersistenceManagerInViewFilter&lt;/filter-name&gt;
      &lt;filter-class&gt;
          org.springframework.orm.jdo.support.OpenPersistenceManagerInViewFilter
      &lt;/filter-class&gt;
      &lt;init-param&gt;
        &lt;param-name&gt;persistenceManagerFactoryBeanName&lt;/param-name&gt;
        &lt;param-value&gt;internalPersistenceManagerFactory&lt;/param-value&gt;
      &lt;/init-param&gt;
  &lt;/filter&gt;
  
  &lt;filter&gt;
      &lt;filter-name&gt;dsmProdOpenPersistenceManagerInViewFilter&lt;/filter-name&gt;
      &lt;filter-class&gt;
          org.springframework.orm.jdo.support.OpenPersistenceManagerInViewFilter
      &lt;/filter-class&gt;
      &lt;init-param&gt;
        &lt;param-name&gt;persistenceManagerFactoryBeanName&lt;/param-name&gt;
        &lt;param-value&gt;dsmProdInternalPersistenceManagerFactory&lt;/param-value&gt;
      &lt;/init-param&gt;
  &lt;/filter&gt;
  
  &lt;filter&gt;
      &lt;filter-name&gt;dsmTestOpenPersistenceManagerInViewFilter&lt;/filter-name&gt;
      &lt;filter-class&gt;
          org.springframework.orm.jdo.support.OpenPersistenceManagerInViewFilter
      &lt;/filter-class&gt;
      &lt;init-param&gt;
        &lt;param-name&gt;persistenceManagerFactoryBeanName&lt;/param-name&gt;
        &lt;param-value&gt;dsmTestInternalPersistenceManagerFactory&lt;/param-value&gt;
      &lt;/init-param&gt;
  &lt;/filter&gt;
  
  &lt;filter&gt;
      &lt;filter-name&gt;onboarderOpenPersistenceManagerInViewFilter&lt;/filter-name&gt;
      &lt;filter-class&gt;
          org.springframework.orm.jdo.support.OpenPersistenceManagerInViewFilter
      &lt;/filter-class&gt;
      &lt;init-param&gt;
        &lt;param-name&gt;persistenceManagerFactoryBeanName&lt;/param-name&gt;
        &lt;param-value&gt;onboarderInternalPersistenceManagerFactory&lt;/param-value&gt;
      &lt;/init-param&gt;
  &lt;/filter&gt;
  
  &lt;filter&gt;
      &lt;filter-name&gt;OpenEntityManagerInViewFilter&lt;/filter-name&gt;
      &lt;filter-class&gt;
          org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
      &lt;/filter-class&gt;
      &lt;init-param&gt;
        &lt;param-name&gt;entityManagerFactoryBeanName&lt;/param-name&gt;
        &lt;param-value&gt;orgDataDir_EntityManagerFactory&lt;/param-value&gt;
      &lt;/init-param&gt;
  &lt;/filter&gt;
  
  &lt;!-- 
      Route all requests through Spring Security for A&amp;A
  --&gt;
  &lt;filter&gt;
      &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
      &lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;/filter-class&gt;
  &lt;/filter&gt;
  
  &lt;!--  
      There are three means to configure Wickets configuration mode and they are
      tested in the order given. 
      1) A system property: -Dwicket.configuration
      2) servlet specific &lt;init-param&gt;
      3) context specific &lt;context-param&gt;
      The value might be either "development" (reloading when templates change)
      or "deployment". If no configuration is found, "development" is the default.
  --&gt;
  &lt;filter&gt;
      &lt;filter-name&gt;wicket&lt;/filter-name&gt;
      &lt;filter-class&gt;
          org.apache.wicket.protocol.http.WicketFilter
      &lt;/filter-class&gt;
      &lt;init-param&gt;
          &lt;param-name&gt;applicationFactoryClassName&lt;/param-name&gt;
          &lt;param-value&gt;
              org.apache.wicket.spring.SpringWebApplicationFactory
          &lt;/param-value&gt;
      &lt;/init-param&gt;
  &lt;/filter&gt;
  
  &lt;!-- The SpringWebApplicationFactory will need access to a Spring Application context, configured like this... --&gt;
  &lt;context-param&gt;
      &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
      &lt;param-value&gt;
          classpath:/applicationSecurityContext.xml
          classpath:/applicationContext.xml
          classpath:/orgDataDir-config.xml
          classpath:/dsm-prod-spring-jdo-config.xml
          classpath:/dsm-test-spring-jdo-config.xml
          classpath:/dsm-prod-beans.xml
          classpath:/dsm-test-beans.xml
          classpath:/exchange-config.xml
          classpath:/reporting.xml
          classpath:/profile-conversion.xml
          classpath:/com/mycompany/connmgmt/integration/jms/jms-client.xml
          classpath:/com/mycompany/connmgmt/integration/jms/test-jms-client.xml
          classpath:/com/mycompany/connmgmt/integration/jms/jms-consumers.xml
          classpath:/com/mycompany/connmgmt/integration/jms/jms-producers.xml
          classpath*:/com/mycompany/core/odd/odd-applicationContext.xml
          classpath:/task-support.xml
      &lt;/param-value&gt;
  &lt;/context-param&gt;
  &lt;listener&gt;
      &lt;listener-class&gt;
          org.springframework.web.context.ContextLoaderListener
      &lt;/listener-class&gt;
  &lt;/listener&gt;
  &lt;!-- Support for session- and request-scoped beans --&gt;
  &lt;listener&gt;
     &lt;listener-class&gt;
          org.springframework.web.context.request.RequestContextListener
     &lt;/listener-class&gt;
  &lt;/listener&gt; 
  
  &lt;!-- 
      Listen for Session events and publish for Spring Security's consumption
  --&gt;
  &lt;listener&gt;
      &lt;listener-class&gt;
          org.springframework.security.web.session.HttpSessionEventPublisher
      &lt;/listener-class&gt;
  &lt;/listener&gt;
  
  &lt;filter-mapping&gt;
      &lt;filter-name&gt;OpenPersistenceManagerInViewFilter&lt;/filter-name&gt;
      &lt;url-pattern&gt;/app/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;
  
  &lt;filter-mapping&gt;
      &lt;filter-name&gt;dsmProdOpenPersistenceManagerInViewFilter&lt;/filter-name&gt;
      &lt;url-pattern&gt;/app/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;
  
  &lt;filter-mapping&gt;
      &lt;filter-name&gt;dsmTestOpenPersistenceManagerInViewFilter&lt;/filter-name&gt;
      &lt;url-pattern&gt;/app/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;
  
  &lt;filter-mapping&gt;
      &lt;filter-name&gt;onboarderOpenPersistenceManagerInViewFilter&lt;/filter-name&gt;
      &lt;url-pattern&gt;/app/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;
  
  &lt;filter-mapping&gt;
      &lt;filter-name&gt;OpenEntityManagerInViewFilter&lt;/filter-name&gt;
      &lt;url-pattern&gt;/app/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;
  
  &lt;filter-mapping&gt;
      &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
      &lt;url-pattern&gt;/app/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;
  
  &lt;filter-mapping&gt;
      &lt;filter-name&gt;wicket&lt;/filter-name&gt;
      &lt;url-pattern&gt;/app/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;
  
  &lt;session-config&gt;
      &lt;!-- 10 hours [TRAC-83] --&gt;
      &lt;session-timeout&gt;600&lt;/session-timeout&gt; 
  &lt;/session-config&gt;
  &lt;/web-app&gt;
  </code></pre>
  
  <p>Note that this is a different problem from related errors:
  This suggests pre-auth-filter, which shouldn't be required per the Spring/CAS docs or example app: <a href="https://stackoverflow.com/questions/8360405/how-to-prevent-ja-sig-cas-spring-security-redirect-loop">How to prevent JA-SIG CAS spring security redirect loop?</a></p>
  
  <p>This mentions that j_spring_cas_security_check needs to be under a URL that's protected by CasAuthenticationFilter: <a href="https://stackoverflow.com/questions/21932944/redirect-loop-with-cas-and-spring-security">Redirect loop with CAS and Spring Security</a></p>
  
  <p>This mentions that a user details service is required, which I have: <a href="https://stackoverflow.com/questions/20915211/redirect-loop-with-grails-spring-security-cas-plugin">Redirect loop with Grails Spring Security CAS plugin</a></p>