2015-09-28 160 views
1

我有Spring Security的LDAP認證並在我們的應用程序運行,一切順利與它從一個側面運行,分開。如果AD服務器恰好處於脫機狀態或不可接觸狀態,則會觸發 ,則會引發org.springframework.ldap.CommunicationException。然而,這個異常沒有被捕獲到,並作爲一個包含堆棧跟蹤的Tomcat HTTP 500錯誤頁面(如下所示)傳播回客戶端。春季安全 - 無法趕上LDAP異常,當AD服務器脫機

我用java配置使用Spring Security 4.0.1,Java的8和Tomcat 7.0.42。

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 

有沒有我們在這個異常跟蹤代碼,甚至進一步上漲的堆棧(我已經爲了簡明扼要省略,因爲它只是在Servlet過濾器鏈)。

通常情況下,我只是繼承Spring ActiveDirectoryLdapAuthenticationProvider類的子類,並在該派生類中調用try/catch中的基類doAuthentication()方法並從那裏繼續。不幸的是ActiveDirectoryLdapAuthenticationProvider被宣佈爲 作爲final,所以我不能這樣做。

我試圖嘗試捕獲該異常在我的代碼嘗試了一些其他的東西。這些是

  1. 添加了一個身份驗證失敗者的formLogin配置如下。但是,這並沒有得到當異常得到投擲叫,似乎只 火基於認證例外,

    ... 
        .formLogin()    
         .usernameParameter("j_username") 
         .passwordParameter("j_password")    
         .loginPage("/login.jsp") 
         .loginProcessingUrl("/j_spring_security_check")   
         .successHandler(authenticationSuccessHandler()) 
         .failureHandler(authenticationFailureHandler()) 
        .and() 
        ... 
    
  2. 搭建authenticationEndpoint與我SecurityConfig下面的代碼的任何未通過認證的請求。 Java文件,

    ... 
        .formLogin()    
         .usernameParameter("j_username") 
         .passwordParameter("j_password")    
         .loginPage("/login.jsp") 
         .loginProcessingUrl("/j_spring_security_check")   
         .successHandler(authenticationSuccessHandler()) 
        .and() 
        .exceptionHandling().authenticationEntryPoint(myAuthenticationEntryPoint()) 
        ... 
    

其中myAuthenticationEntryPoint()方法是,僅僅返回一個新Http403ForbiddenEntryPoint一個@Bean批註的方法。但是,這不會被稱爲 ,我仍然收到HTTP 500錯誤,而不是來自配置的身份驗證入口點的403錯誤。

如下所示拋出在Spring Security的源代碼異常的實際方法:

private DirContext bindAsUser(String username, String password) { 
    // TODO. add DNS lookup based on domain 
    final String bindUrl = url; 

    Hashtable<String,String> env = new Hashtable<String,String>(); 
    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); 
     } 
    } 
} 

對於在AD服務器不接觸的情況下,則代碼進入在異常處理程序中的其他條款我們得到一個基於Ldap的異常。據我所見,這意味着沒有任何「正常」的身份驗證失敗處理程序被調用。

我發現捕獲這個異常的唯一方法是'fork'ActiveDirectoryLdapAuthenticationProvider類,即剪切+粘貼它的內容到一個新類中,並從中派生出一個類。這樣做可以按預期工作,我可以在try/catch中捕獲異常。但我真的不想去這個選項,因爲它只是一個黑客。

有沒有人有類似的情況,發現了一個更好的解決方案呢?我不禁想到,在登錄過程中,必須有辦法處理所有異常,而不僅僅是基於身份驗證的方式,但我一直無法找到一種方法來執行此操作。

+0

我有同樣的問題,所以我必須實現我自己的'AuthenticationProvider',也許把自己的夜錯誤頁面在web.xml將幫助您:' \t \t \t \t /error.htm \t @ –

+0

@DavidHerrero,謝謝你的回覆。我希望在我的配置中只有一個缺失的部分。不得不實現我自己的AuthenticationProvider來捕獲異常似乎相當重量級。不是我懷疑你在說什麼:),也許這是唯一的方法。 – user3262600

+0

我會寫一個可以幫助你的小答案。 –

回答

0

你只是在@kpentchev答案寫這樣的:當這個異常被拋出

我預期的行爲是,我可以 捕捉它,並返回給用戶一個合適的錯誤到登錄頁面。 而不是使用只看到藍色的Tomcat 500錯誤頁面和 異常跟蹤。

如果你只是想「塊」是默認的Tomcat錯誤頁面,讓攻擊者或用戶無法看到您的服務器的重要信息,你可以把這個在你的web.xml:

<error-page> 
<error-code>500</error-code> 
<location>/error.jsp</location> 
</error-page> 

後,在你的error.jsp頁面,您可以檢查與異常:

Throwable throwable = (Throwable) request 
       .getAttribute("javax.servlet.error.exception"); 

而且將用戶重定向到登錄頁面的情況下,拋出是CommunicationException.class

實例3210
+0

是的,這是做我想做的事情,事實上,它發送用戶回登錄。這個錯誤發生時,jsp頁面。很遺憾,你不能在Spring Security java配置中處理這個異常,並且你必須放入一個jsp文件。然而,它應該是一個非常罕見的事件,現在它實際上被處理了。所以我接受這個答案,謝謝你的幫助David :) – user3262600

0

您可以定義處理ldap.CommunicationExceptionControllerAdvice。您可以指定響應或查看每個異常類型。

@ControllerAdvice 
public class LdapErrorController { 

    @ExceptionHandler({CommunicationException.class}) 
    public ResponseEntity<ErrorMessage> handleLdapCommunicationException(Exception ex) { 
     return new ResponseEntity<ErrorMessage>(new ErrorMessage("Can not connect to LDAP server."), HttpStatus.INTERNAL_SERVER_ERROR); 
    } 

} 

該示例將根據Request的Accept報頭返回響應正文中序列化的ErrorMessage。如果您想提供視圖,可以直接返回ModelAndViewRedirectView

+0

感謝您的響應。但是,這不是Spring MVC應用程序。我只是使用Spring Security來保護基於Angular JS的應用程序。我不確定是否可以將ExceptionHandler註釋與我從Spring Security類派生的類一起使用。我會放棄它。 – user3262600

+0

那麼既然你使用'spring.security.web',你也使用'spring.webmvc'。你如何使它與angularjs一起工作是另一個問題。發生此異常時,您的預期行爲是什麼? – kpentchev

+0

「那麼既然你使用的是spring.security.web,你也使用了spring.webmvc」,我不認爲這是真的。我的pom文件中沒有spring-webmvc工件。就像我提到的那樣,除了這個問題之外,應用程序運行良好。我拋出這個異常時的預期行爲是我可以捕獲它,並返回用戶登錄頁面,並帶有適當的錯誤。而不是隻使用異常追蹤看到藍色的Tomcat 500錯誤頁面。 – user3262600