无法使用Spring处理程序捕获LDAP CommunicationException

时间:2019-11-14 19:29:54

标签: java spring-boot spring-security spring-ldap spring-security-ldap

当前,我在安全认证过程中使用ActiveDirectoryLdapAuthenticationProvider和一个自定义提供程序。假设CustomAuthenticationProvider引发异常,则安全流进入ldapAuthenticationProvider。 我有一个问题,当没有连接到AD服务器时,我得到CommunicationException,无法处理:

a)由@ControllerAdvice@ExceptionHandler

b)由AuthenticationFailureHandler

c)AuthenticationEntryPoint

我在控制台和默认弹簧“错误”的回调中遇到异常。我假设它是在后台某处完成的,但是我对此无能为力,无法找到原因。我想捕获CommunicationException并扔掉CustomException(或任何其他)以解决javascript客户端上的消息。我该怎么办?

callbackUrl

SecurityConfig:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.exceptionHandling()
            .and().authorizeRequests()
            .antMatchers("...").permitAll()
            .anyRequest().authenticated()
            .and().formLogin().loginPage("/login")
            .failureHandler(new CustomFailureHandler())
            .successHandler(new CustomSuccessHandler()).permitAll()
            .and().logout().permitAll()
            .and().exceptionHandling().authenticationEntryPoint(unauthorizedHandler);
}

@Override
protected void configure(AuthenticationManagerBuilder auth) {
    auth
            .authenticationProvider(new CustomAuthenticationProvider())
            .authenticationProvider(ldapAuthenticationProvider());
}

UnauthorizedHandler:

@Component
public class UnauthorizedHandler implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request,
                        HttpServletResponse response,
                        AuthenticationException authException) throws IOException {
        String[] url = request.getRequestURL().toString().split(request.getContextPath());
        String redirect = request.getContextPath();
        if (url.length == 2) redirect = request.getContextPath() + "/?callbackUrl=" + url[1].substring(1);
        response.sendRedirect(redirectUrl);
    }
}

CustomFailureHandler:

@Component
public class CustomFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest,
                                        HttpServletResponse httpServletResponse,
                                        AuthenticationException exception) throws IOException {
        String message = "...";

        if (exception instanceof BadCredentialsException) {
            message = "...";
        } else if (exception instanceof CommunicationException) {
            message = "...";
        }

        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setStatus(500);
        httpServletResponse.getWriter().print(message);
        httpServletResponse.getWriter().flush();
        httpServletResponse.getWriter().close();
    }
}

例外:

org.springframework.ldap.CommunicationException: 10.1.3.13:389; nested exception is javax.naming.CommunicationException: 10.1.3.13:389 [Root exception is java.net.ConnectException: Connection timed out: connect]
    at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:108)
    at org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.bindAsUser(ActiveDirectoryLdapAuthenticationProvider.java:215)
    at org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.doAuthentication(ActiveDirectoryLdapAuthenticationProvider.java:146)
    at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:85)
    at org........authenticate(ActiveDirectoryLdapAuthenticationProvider.java:29)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:200)
    at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:124)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.base/java.lang.Thread.run(Thread.java:835)
Caused by: javax.naming.CommunicationException: 10.1.3.13:389
    at java.naming/com.sun.jndi.ldap.Connection.<init>(Connection.java:237)
    at java.naming/com.sun.jndi.ldap.LdapClient.<init>(LdapClient.java:137)
    at java.naming/com.sun.jndi.ldap.LdapClient.getInstance(LdapClient.java:1610)
    at java.naming/com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2752)
    at java.naming/com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:320)
    at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxFromUrl(LdapCtxFactory.java:225)
    at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:189)
    at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:243)
    at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:154)
    at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:84)
    at java.naming/javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:730)
    at java.naming/javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:305)
    at java.naming/javax.naming.InitialContext.init(InitialContext.java:236)
    at java.naming/javax.naming.ldap.InitialLdapContext.<init>(InitialLdapContext.java:154)
    at org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider$ContextFactory.createContext(ActiveDirectoryLdapAuthenticationProvider.java:426)
    at org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.bindAsUser(ActiveDirectoryLdapAuthenticationProvider.java:206)
    ... 47 common frames omitted
Caused by: java.net.ConnectException: Connection timed out: connect
    at java.base/java.net.PlainSocketImpl.connect0(Native Method)
    at java.base/java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:101)
    at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399)
    at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242)
    at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224)
    at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:403)
    at java.base/java.net.Socket.connect(Socket.java:591)
    at java.base/java.net.Socket.connect(Socket.java:540)
    at java.base/java.net.Socket.<init>(Socket.java:436)
    at java.base/java.net.Socket.<init>(Socket.java:213)
    at java.naming/com.sun.jndi.ldap.Connection.createSocket(Connection.java:330)
    at java.naming/com.sun.jndi.ldap.Connection.<init>(Connection.java:216)
    ... 62 common frames omitted

1 个答案:

答案 0 :(得分:1)

CommunicationException不是AuthenticationException的实例,所以该行

if (exception instanceof CommunicationException) {

永远不会过去。

捕获此异常的简单方法是将LDAP身份验证提供程序包装在您自己的包装中:

public class MyLdapAuthenticationProvider implements AuthenticationProvider {
    private final ActiveDirectoryLdapAuthenticationProvider delegate;

    public MyLdapAuthenticationProvider(
        ActiveDirectoryLdapAuthenticationProvider delegate) {
        this.delegate = delegate;
    }

    public Authentication authenticate(Authentication authentication) {
        try {
            return this.delegate.authenticate(authentication);
        } catch (CommunicationException e) {
            throw new InternalAuthenticationServiceException(e);
        }
    }
}

然后:

@Override
protected void configure(AuthenticationManagerBuilder auth) {
    auth
        .authenticationProvider(new CustomAuthenticationProvider())
        .authenticationProvider(
            new MyLdapAuthenticationProvider(ldapAuthenticationProvider()));
}

现在说,Spring Security中有一个开放票证,以便CommunicationException is correctly wrapped in an InternalAuthenticationServiceException。您可能会考虑提供一个修补程序,以便不需要委托的身份验证提供程序。