title: Infinite loop in custom Spring Security application
body: <p>We try to substitute an existing Spring Security Basic Login for a REST-API in an Open Source Application to achieve a custom login with a token. I read this blogpost about the topic: <a href="http://javattitude.com/2014/06/07/spring-security-custom-token-based-rest-authentication/" rel="nofollow">http://javattitude.com/2014/06/07/spring-security-custom-token-based-rest-authentication/</a></p>
  
  <p>When the request has no header named "Cookie", I get correcty a 401 - unauthorized response (expected behaviour). When the request has a valid token, I get an infinite loop causing a <code>java.lang.StackOverflowError</code>:</p>
  
  <pre><code>    Exception in thread "http-bio-8080-exec-45" java.lang.StackOverflowError
          at org.apache.tomcat.util.http.NamesEnumerator.&lt;init&gt;(MimeHeaders.java:402)
          at org.apache.tomcat.util.http.MimeHeaders.names(MimeHeaders.java:228)
          at org.apache.catalina.connector.Request.getHeaderNames(Request.java:2108)
          at org.apache.catalina.connector.RequestFacade.getHeaderNames(RequestFacade.java:726)
          at javax.servlet.http.HttpServletRequestWrapper.getHeaderNames(HttpServletRequestWrapper.java:103)
          at javax.servlet.http.HttpServletRequestWrapper.getHeaderNames(HttpServletRequestWrapper.java:103)
          at javax.servlet.http.HttpServletRequestWrapper.getHeaderNames(HttpServletRequestWrapper.java:103)
          at javax.servlet.http.HttpServletRequestWrapper.getHeaderNames(HttpServletRequestWrapper.java:103)
          at javax.servlet.http.HttpServletRequestWrapper.getHeaderNames(HttpServletRequestWrapper.java:103)
          at javax.servlet.http.HttpServletRequestWrapper.getHeaderNames(HttpServletRequestWrapper.java:103)
  at org.activiti.rest.security.CustomTokenAuthenticationFilter.attemptAuthentication(CustomTokenAuthenticationFilter.java:43)
      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:211)
      at org.activiti.rest.security.CustomTokenAuthenticationFilter.doFilter(CustomTokenAuthenticationFilter.java:86)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:65)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
      at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:166)
      at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
      at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
      at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:749)
      at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:487)
      at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:412)
      at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:339)
      at org.springframework.security.web.firewall.RequestWrapper$FirewalledRequestAwareRequestDispatcher.forward(RequestWrapper.java:132)
      at org.activiti.rest.security.TokenSimpleUrlAuthenticationSuccessHandler.onAuthenticationSuccess(TokenSimpleUrlAuthenticationSuccessHandler.java:30)
      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:331)
      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:298)
      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:235)
      at org.activiti.rest.security.CustomTokenAuthenticationFilter.doFilter(CustomTokenAuthenticationFilter.java:86)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:65)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
      at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:166)
      at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
      at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
      at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:749)
      at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:487)
      at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:412)
      at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:339)
      at org.springframework.security.web.firewall.RequestWrapper$FirewalledRequestAwareRequestDispatcher.forward(RequestWrapper.java:132)
      at org.activiti.rest.security.TokenSimpleUrlAuthenticationSuccessHandler.onAuthenticationSuccess(TokenSimpleUrlAuthenticationSuccessHandler.java:30)
      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:331)
      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:298)
      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:235)
      at org.activiti.rest.security.CustomTokenAuthenticationFilter.doFilter(CustomTokenAuthenticationFilter.java:86)
  </code></pre>
  
  <p>My Spring Security Configuration looks like this:</p>
  
  <pre><code>@Configuration
  @EnableWebSecurity
  @EnableWebMvcSecurity
  public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  
    @Bean
    public AuthenticationProvider authenticationProvider() {
      return new BasicAuthenticationProvider();
    }
  
    @Autowired
    AuthenticationProvider basicAuthenticationProvider;
  
    @Bean
    public CustomTokenAuthenticationFilter customTokenAuthenticationFilter(){
        System.out.println("+++ create new CustomTokenAuthenticationFilter for path=/**");
        return new CustomTokenAuthenticationFilter("/**");
    };
  
    @Autowired
    CustomTokenAuthenticationFilter customTokenAuthenticationFilter;
  
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("init of http security START");
       http
       .authenticationProvider(authenticationProvider())
       .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
       .csrf().disable()
       .authorizeRequests()
         .anyRequest().authenticated()
       .and()//.authenticationProvider(basicAuthenticationProvider);
       .addFilterBefore(customTokenAuthenticationFilter,  BasicAuthenticationFilter.class)
       .httpBasic();
         //.and().addFilter(filter);
      System.out.println("init of http security DONE");
    }
  }
  </code></pre>
  
  <p>I already tried to change the URL-Mapping from <code>/**</code> to <code>/activiti-rest/**</code> but then, the Basic Authentication kicks in again.</p>
  
  <p>This is my custom Token Authentication filter:</p>
  
  <pre><code>public class CustomTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
  
      private static final Logger logger = LoggerFactory.getLogger(CustomTokenAuthenticationFilter.class);
      public CustomTokenAuthenticationFilter(String defaultFilterProcessesUrl) {
          super(defaultFilterProcessesUrl);
          super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(defaultFilterProcessesUrl));
          setAuthenticationManager(new NoOpAuthenticationManager());
          setAuthenticationSuccessHandler(new TokenSimpleUrlAuthenticationSuccessHandler());
      }
  
  
      public final String HEADER_SECURITY_TOKEN = "Cookie";//"LdapToken"; 
  
  
      /**
       * Attempt to authenticate request - basically just pass over to another method to authenticate request headers 
       */
      @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
          Enumeration&lt;String&gt; headerNames = request.getHeaderNames();
          int i = 0;
          while (headerNames.hasMoreElements()){
              String key = (String) headerNames.nextElement();
              String value = request.getHeader(key);
              System.out.println("+++ key["+i+"]" +key);
              System.out.println("+++ val["+i+"]" +value);
              i++;
          }
          String token = request.getHeader(HEADER_SECURITY_TOKEN);
          logger.info("token found:"+token);
          System.out.println("+++ token found:"+token);
          AbstractAuthenticationToken userAuthenticationToken = authUserByToken(token);
          if(userAuthenticationToken == null) throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
          System.out.println("+++ userAuthenticationToken:"+userAuthenticationToken.toString());
          return userAuthenticationToken;
      }
  
  
      /**
       * authenticate the user based on token
       * @return
       */
      private AbstractAuthenticationToken authUserByToken(String token) {
          if(token==null) {
              System.out.println("+++ i shouldn't be null +++");
              return null;
          }
          AbstractAuthenticationToken authToken = new JWTAuthenticationToken(token);
          try {
              return authToken;
          } catch (Exception e) {
              System.out.println(e);
              logger.error("Authenticate user by token error: ", e);
          }
          return authToken;
      }
  
  
      @Override
      public void doFilter(ServletRequest req, ServletResponse res,
              FilterChain chain) throws IOException, ServletException {
          System.out.println("++++++++++++++++++++++++++++++ doFilter ");
          super.doFilter(req, res, chain);
      }
  }
  </code></pre>
  
  <p>And my Custom Success handler. I think that this causes the infinite loop, but I cannot figure out, why:</p>
  
  <pre><code>public class TokenSimpleUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
  
      @Override
      protected String determineTargetUrl(HttpServletRequest request,
              HttpServletResponse response) {
          System.out.println("+++ yuhuuu determineTargetUrl+++");
          String context = request.getContextPath();
          String fullURL = request.getRequestURI();
          String url = fullURL.substring(fullURL.indexOf(context)+context.length());
          return url;
      }
  
      @Override
      public void onAuthenticationSuccess(HttpServletRequest request,
              HttpServletResponse response, Authentication authentication)
              throws IOException, ServletException {
          System.out.println("+++ yuhuuu onAuthenticationSuccess+++");
          String url = determineTargetUrl(request,response);
          request.getRequestDispatcher(url).forward(request, response);
      }
  }
  </code></pre>
  
  <p>All other classes (NoOpAuthenticationManager and RestAuthenticationEntryPoint) are exactly like in this blogpost.</p>
  
  <p>Would be great I someone could give me a hint what could cause this infinite loop. As I said, it only occurs when the Request has a valid token.</p>
  
  <p>Thanks and best regards
  Ben</p>