title: Spring Security - unable to catch LDAP exception when AD server offline
body: <p>I have Spring Security LDAP authentication up and running in our application and everything is running smoothly with it, apart from one aspect. That is
  if the AD Server happens to be offline or uncontactable then a org.springframework.ldap.CommunicationException is thrown. However this exception is uncaught 
  and propagates back to the client as a Tomcat HTTP 500 error page including the stack trace (shown below).</p>
  
  <p>I'm using java config with Spring Security 4.0.1, Java 8 and Tomcat 7.0.42.</p>
  
  <pre><code>springframework.ldap.CommunicationException: localhost:389; nested exception is javax.naming.CommunicationException: localhost:389 [Root exception is java.net.ConnectException: Connection refused: connect]
  org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:108)
  org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.bindAsUser(ActiveDirectoryLdapAuthenticationProvider.java:211)
  org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.doAuthentication(ActiveDirectoryLdapAuthenticationProvider.java:143)
  org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:82)
  org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
  org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:192)
  org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:93)
  ... other frames omitted
  </code></pre>
  
  <p>There's none of our code on this exception trace, even further up the stack (which I've omitted for brevity as it's just the Servlet filter chain).</p>
  
  <p>Normally I this kind of situation I would just subclass the Spring ActiveDirectoryLdapAuthenticationProvider class and in that derived class call
  the base class doAuthentication() method inside a try/catch and go from there. Unfortunately ActiveDirectoryLdapAuthenticationProvider is declared 
  as final so I can't do this.</p>
  
  <p>I've tried a few other things in an attempt to try and catch that exception in my code. These are</p>
  
  <ol>
  <li><p>Added an authenticationfailurehandler the formLogin configuration as below. But this doesn't get called when the exception gets thrown, it only seems
  to fire for authentication based exceptions,</p>
  
  <pre><code>    ...
      .formLogin()            
          .usernameParameter("j_username")
          .passwordParameter("j_password")            
          .loginPage("/login.jsp")
          .loginProcessingUrl("/j_spring_security_check")         
          .successHandler(authenticationSuccessHandler())
          .failureHandler(authenticationFailureHandler())
      .and()
      ...
  </code></pre></li>
  <li><p>Set up an authenticationEndpoint for any non-authenticated requests with the following code in my SecurityConfig.java file,</p>
  
  <pre><code>    ...
      .formLogin()            
          .usernameParameter("j_username")
          .passwordParameter("j_password")            
          .loginPage("/login.jsp")
          .loginProcessingUrl("/j_spring_security_check")         
          .successHandler(authenticationSuccessHandler())
      .and()
      .exceptionHandling().authenticationEntryPoint(myAuthenticationEntryPoint())
      ...
  </code></pre></li>
  </ol>
  
  <p>where the myAuthenticationEntryPoint() method is a @Bean annotated method that just returns a new Http403ForbiddenEntryPoint. But again this doesn't get called 
  and I still get an HTTP 500 error back rather than a 403 from the configured authentication entry point.</p>
  
  <p>The actual method that throws the exception in the Spring Security source code is shown below:</p>
  
  <pre><code>private DirContext bindAsUser(String username, String password) {
      // TODO. add DNS lookup based on domain
      final String bindUrl = url;
  
      Hashtable&lt;String,String&gt; env = new Hashtable&lt;String,String&gt;();
      env.put(Context.SECURITY_AUTHENTICATION, "simple");
      String bindPrincipal = createBindPrincipal(username);
      env.put(Context.SECURITY_PRINCIPAL, bindPrincipal);
      env.put(Context.PROVIDER_URL, bindUrl);
      env.put(Context.SECURITY_CREDENTIALS, password);
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
      env.put(Context.OBJECT_FACTORIES, DefaultDirObjectFactory.class.getName());
  
      try {
          return contextFactory.createContext(env);
      } catch (NamingException e) {
          if ((e instanceof AuthenticationException) || (e instanceof OperationNotSupportedException)) {
              handleBindException(bindPrincipal, e);
              throw badCredentials(e);
          } else {
              throw LdapUtils.convertLdapException(e);
          }
      }
  }
  </code></pre>
  
  <p>For the case where the AD server is not contactable then the code goes into the else clause in the exception handler and we get an Ldap based exception. As far as I can see this means that none of the 'normal' authentication failure handlers get called.</p>
  
  <p>The only way I found to catch this exception was to 'fork' the ActiveDirectoryLdapAuthenticationProvider class, ie cut + paste it's contents into a new class and derive a class from that. Doing that works as expected and I can catch the exception in a try/catch. But I don't really want to go for this option as it's just a hack.</p>
  
  <p>Has anyone had a similar situation and found a more elegant solution to it? I can't help but think that there must be a way to handle ALL exceptions during
  the logon process, not just authentication based ones, but I've been unable to find a way to do this. </p>