2015-08-27 156 views
1

在我用Spring Security進行安全保護的Spring MVC應用程序中,我必須通過以下方式登錄:一是通過識別特定授權IP列表(不需要用戶/密碼登錄),一個是通過經典登錄。爲此,我構建了一個自定義身份驗證提供程序來驗證IP是否經過授權。如果不是,我希望它重定向到通知用戶的錯誤頁面。ExceptionMappingAuthenticationFailureHandler沒有捕捉到我的異常

在這種情況下,我拋出異常(org.springframework.security.authentication.InsufficientAuthenticationException),並且我試圖使用ExceptionMappingAuthenticationFailureHandler來捕捉它並將其重定向到正確的頁面。失敗處理程序將InsufficientAuthenticationException映射到該頁面,並將BadCredentialsException映射到另一個錯誤頁面(用於執行經典用戶/密碼登錄和登錄錯誤時)。

我用日誌檢查我實際上是拋出異常。但是,失敗處理程序永遠不會捕獲它;相反,我總是會獲得專門用於BadCredentialsException的頁面。 如何確保我拋出的異常被失敗處理程序捕獲?

這裏是我的安全context.xml中的摘錄:

<http 
    auto-config="true" 
    use-expressions="true"> 
    <form-login 
     login-page="/pages/login.jsp" 
     login-processing-url="/j_spring_security_check" 
     default-target-url="/" 
     authentication-failure-handler-ref="authenticationFailureHandler" /> 
</http> 

<beans:bean 
    id="customIPAddressAuthenticationProvider" 
    class="ch.of.ow.security.CustomIPAddressAuthenticationProvider" /> 

<beans:bean 
    id="authenticationFailureHandler" 
    class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler"> 
    <beans:property name="exceptionMappings"> 
     <beans:props> 
      <beans:prop 
       key="org.springframework.security.authentication.InsufficientAuthenticationException"> 
       /notValidIP 
      </beans:prop> <!-- THIS IS THE PAGE I WANT --> 
      <beans:prop 
       key="org.springframework.security.authentication.BadCredentialsException"> 
       /pages/login.jsp?login_error=true 
      </beans:prop> <!-- THIS IS THE PAGE I GET INSTEAD --> 
     </beans:props> 
    </beans:property> 
</beans:bean> 


<authentication-manager alias="authenticationManager"> 
    <authentication-provider ref="customIPAddressAuthenticationProvider" /> 
    <authentication-provider user-service-ref='userDetailsService'> 
    <password-encoder hash="bcrypt" /> 
    </authentication-provider> 
</authentication-manager> 

這裏是CustomIPAddressAuthenticationProvider的相關部分:

public class CustomIPAddressAuthenticationProvider implements AuthenticationProvider { 

    @Override 
    public Authentication authenticate(Authentication authentication) throws AuthenticationException, InsufficientAuthenticationException { 
     if (!authentication.getPrincipal().equals("") || !authentication.getCredentials().equals("")) { 
      // if the user did provide a username/password, skip to the next 
      // authentication provider, as he will want to log in with his 
      // credentials instead of his IP 
      return null; 
     } 
     WebAuthenticationDetails wad = null; 
     String userIPAddress = null; 
     boolean isAuthenticatedByIP = false; 

     // Get the IP address of the user tyring to use the site 
     wad = (WebAuthenticationDetails) authentication.getDetails(); 
     userIPAddress = wad.getRemoteAddress(); 
     // Check if the user is authorized 
     isAuthenticatedByIP = ipIsAuthorized(userIPAddress); //the ipIsAuthorized does its stuff well 

     // Authenticated, the user's IP address is authorized 
     if (isAuthenticatedByIP) { 
      List<GrantedAuthority> grantedAuths = new ArrayList<>(); 
      grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER")); 
      Authentication result = new UsernamePasswordAuthenticationToken("xxxxxxxxx", "yyyyyyyyy", grantedAuths); 
      return result; 
     } else { 
      System.out.println("Going to throw InsufficientAuthenticationException"); 
      throw new InsufficientAuthenticationException(userIPAddress); 
     } 
    } 

} 

而且我注意到,如果我刪除的第二映射失敗處理程序,我只留下它:

  <beans:prop 
       key="org.springframework.security.authentication.InsufficientAuthenticationException"> 
       /notValidIP 
      </beans:prop> 

然後我s即,如果我嘗試使用不可信IP訪問,我會從/j_spring_security_check獲得HTTP Status 401 - Authentication Failed: Bad credentials。所以確實是這種例外。我是否可能在錯誤的地方拋出我的例外?在身份驗證提供程序中引發異常是否意味着在鏈中觸發了更高的憑證異常?

在此先感謝您提供的任何建議。

回答

1

好吧,我想通了。如果您有多個身份驗證提供程序,Spring的ProviderManager負責遍歷它們直到找到提供有效身份驗證的身份驗證。 如果你在其中一個提供者中拋出異常,我會這樣做,ProviderManager會捕獲它;然而,除非例外是AccountStatusExceptionInternalAuthenticationServiceException,的實例(或子類),否則它將繼續提供給提供商列表。通過查看ProviderManager的源代碼可以瞭解到這一點。

在我而言,這意味着我扔InsufficientAuthenticationException(這既不是的AccountStatusException也不InternalAuthenticationServiceException子類),則ProviderManager會趕上它不停止,並移動到userDetailsService提供商;由於沒有輸入登錄信息,這總是會導致BadCredentialsException,然後在ExceptionMappingAuthenticationFailureHandler中出現。

的解決方案是:

  • 創建IPNotTrustedException延伸DisabledException
  • 投擲IPNotTrustedExceptionCustomIPAddressAuthenticationProvider
  • 映射IPNotTrustedException/notValidIP URL我想趕快。

當然,不要忘記設置/notValidIP無需身份驗證即可訪問。奇蹟般有效。 :)