春天启动与会话/ Redis的序列化错误与错误的Active Directory LDAP凭证错误、凭证、春天、序列化

2023-09-08 13:06:03 作者:半侧阳光半缕悠然

您好我是新来的春天和Java,我想实现一个网关的认证服务器在本教程中的 https://spring.io/guides/tutorials/spring-security-and-angular-js/

我得到了一切工作,然后试图执行针对我们公司的LDAP服务器的身份验证。它的工作原理,如果我用一个有效的用户名和密码。当我使用无效的凭证的应用程序错误。

我不是在工作,所以我没有确切的错误,但它返回一个LDAP错误(com.sun.jndi.ldap.LdapCtx)和Redis的试图序列化。

时有什么我在我的配置中缺少。从我读过,我想我应该寻找一种方式来包装/扩展类并实现Serializable接口,但我不能确定的最小侵入性的方式来使用Spring引导做到这一点。

任何帮助是极大的AP preciated。

谢谢

迈克·科瓦尔斯基

PS我一直在工作主要是在动态语言和框架,到现在为止(使用Javascript /节点,PHP / Laravel)

下面是什么,我觉得是安全配置的相关部分:

  @Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
公共类SecurityConfiguration扩展WebSecurityConfigurerAdapter {

  @覆盖
  保护无效配置(HttpSecurity HTTP)抛出异常{
    HTTP
      .formLogin()
      .defaultSuccessUrl(/)
      .loginPage(/登录)
      .permitAll()
      。和()
      。登出()
      .logoutSuccessUrl(/注销)
      .permitAll();


    HTTP
      .authorizeRequests()
        .antMatchers(/登录)。permitAll()
        .anyRequest()。验证()
    。和()
      .csrf()。csrfTokenRepository(csrfTokenRepository())
    。和()
      .addFilterAfter(csrfHeaderFilter(),CsrfFilter.class);
  }

  @覆盖
   保护无效配置(AuthenticationManagerBuilder authManagerBuilder)抛出异常{
    authManagerBuilder
      .authenticationProvider(activeDirectoryLdapAuthenticationProvider())
        .userDetailsS​​ervice(UserDetailsS​​ervice的());
   }

   @豆
   公共AuthenticationManager的AuthenticationManager会(){
      返回新的ProviderManager(
        Arrays.asList(activeDirectoryLdapAuthenticationProvider())
      );
   }

   @豆
   公众的AuthenticationProvider activeDirectoryLdapAuthenticationProvider(){
  ActiveDirectoryLdapAuthenticationProvider提供商=新ActiveDirectoryLdapAuthenticationProvider(
    XXX.XXX,LDAPS://XXX.XXX:636);
      provider.setConvertSubError codesToExceptions(真正的);
      provider.setUseAuthenticationRequestCredentials(真正的);
      返回供应商;
   }

  私人过滤csrfHeaderFilter(){
    返回新OncePerRequestFilter(){
      @覆盖
      保护无效doFilterInternal(
        HttpServletRequest的要求,
        HttpServletResponse的响应,
        的FilterChain的FilterChain
      )抛出了ServletException,IOException异常
      {
        CsrfToken CSRF =(CsrfToken)request.getAttribute(CsrfToken.class
            .getName());
        如果(CSRF!= NULL){
          饼干饼干= WebUtils.getCookie(要求,XSRF-TOKEN);
          字符串标记= csrf.getToken();
          如果(饼干== NULL ||令牌!= NULL
              &功放;&安培; !token.equals(cookie.getValue())){
            饼干=新的Cookie(XSRF-TOKEN,令牌);
            cookie.setPath(/);
            response.addCookie(饼干);
          }
        }
        filterChain.doFilter(请求,响应);
      }
    };
  }

  私人CsrfTokenRepository csrfTokenRepository(){
    HttpSessionCsrfTokenRepository库=新HttpSessionCsrfTokenRepository();
    repository.setHeaderName(X-XSRF-TOKEN);
    返回库;
  }

}
 

下面是使用无效的凭证错误的一部分:

  2015年9月24日15:07:30.564 DEBUG 6552 --- [NIO-8080-EXEC-3] ossecurity.web.FilterChainProxy:/登录在13位3额外的过滤器链;发射过滤器:HeaderWriterFilter
2015年9月24日15:07:30.564 DEBUG 6552 --- [NIO-8080-EXEC-3] osswheader.writers.HstsHeaderWriter:不注射HSTS头,因为它不符合requestMatcher org.springframework.security.web。 header.writers.HstsHeaderWriter$SecureRequestMatcher@44258b05
2015年9月24日15:07:30.564 DEBUG 6552 --- [NIO-8080-EXEC-3] ossecurity.web.FilterChainProxy:/登录在13额外的过滤器链中的位置4;发射过滤器:CsrfFilter
2015年9月24日15:07:30.564 DEBUG 6552 --- [NIO-8080-EXEC-3] ossecurity.web.FilterChainProxy:/登录在13额外的过滤器链中的位置5;射击过滤器:''
2015年9月24日15:07:30.564 DEBUG 6552 --- [NIO-8080-EXEC-3] ossecurity.web.FilterChainProxy:/登录在13额外的过滤器链中的位置6;发射过滤器:LogoutFilter
2015年9月24日15:07:30.564 DEBUG 6552 --- [NIO-8080-EXEC-3] osswumatcher.AntPathRequestMatcher:检查请求的匹配:'/登录;反对/注销
2015年9月24日15:07:30.580 DEBUG 6552 --- [NIO-8080-EXEC-3] ossecurity.web.FilterChainProxy:/登录在13额外的过滤器链中的位置7;发射过滤器:UsernamePasswordAut​​henticationFilter
2015年9月24日15:07:30.580 DEBUG 6552 --- [NIO-8080-EXEC-3] osswumatcher.AntPathRequestMatcher:检查请求的匹配:'/登录;反对/登录
2015年9月24日15:07:30.580 DEBUG 6552 --- [NIO-8080-EXEC-3] waUsernamePasswordAut​​henticationFilter:请求是处理身份验证
2015年9月24日15:07:30.580 DEBUG 6552 --- [NIO-8080-EXEC-3] ossauthentication.ProviderManager:身份验证尝试使用org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider
2015年9月24日15:07:30.580 DEBUG 6552 --- [NIO-8080-EXEC-3] ctiveDirectoryLdapAuthenticationProvider:用户处理身份验证请求:管理员
2015年9月24日15:07:31.113 DEBUG 6552 --- [NIO-8080-EXEC-3] ctiveDirectoryLdapAuthenticationProvider:身份验证admin@countrycurtains.local失败:javax.naming.AuthenticationException:[LDAP:错误code 49 -  80090308:LdapErr:DSID-0C090334,注释:AcceptSecurityContext错误,数据525,vece]
2015年9月24日15:07:31.113 INFO 6552 --- [NIO-8080-EXEC-3] ctiveDirectoryLdapAuthenticationProvider:失败Active Directory身份验证:用户未在目录中找到
2015年9月24日15:07:31.114 DEBUG 6552 --- [NIO-8080-EXEC-3] waUsernamePasswordAut​​henticationFilter:身份验证请求失败:org.springframework.security.authentication.BadCredentialsException:坏凭据
2015年9月24日15:07:31.114 DEBUG 6552 --- [NIO-8080-EXEC-3] waUsernamePasswordAut​​henticationFilter:更新SecurityContextHolder中包含空认证
2015年9月24日15:07:31.114 DEBUG 6552 --- [NIO-8080-EXEC-3] waUsernamePasswordAut​​henticationFilter:委派认证失败处理org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@28626d9a
2015年9月24日15:07:31.114 DEBUG 6552 --- [NIO-8080-EXEC-3] .a.SimpleUrlAuthenticationFailureHandler:重定向到/密码错误
2015年9月24日15:07:31.115 DEBUG 6552 --- [NIO-8080-EXEC-3] ossweb.DefaultRedirectStrategy:重定向到'?/登录错误
2015年9月24日15:07:31.115 DEBUG 6552 --- [NIO-8080-EXEC-3] wcHttpSessionSecurityContextRepository:SecurityContext的是空的或内容是匿名的 - 环境不会被存储在HttpSession中。
2015年9月24日15:07:31.139 DEBUG 6552 --- [NIO-8080-EXEC-3] sswcSecurityContextPersistenceFilter:SecurityContextHolder中现已清除,按要求处理完毕
2015年9月24日15:07:31.148错误6552 --- [NIO-8080-EXEC-3] oaccC [[[/] [DispatcherServlet的]:。Servlet.service()进行的servlet [DispatcherServlet会在上下文路径[]抛出异常

org.springframework.data.redis.serializer.SerializationException:无法序列;嵌套例外是org.springframework.core.serializer.support.SerializationFailedException:无法使用序列化对象DefaultSerializer;嵌套例外是java.io.NotSerializableException:com.sun.jndi.ldap.LdapCtx
        在org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:52)
        在org.springframework.data.redis.core.AbstractOperations.rawHashValue(AbstractOperations.java:146)
        在org.springframework.data.redis.core.DefaultHashOperations.putAll(DefaultHashOperations.java:128)
        在org.springframework.data.redis.core.DefaultBoundHashOperations.putAll(DefaultBoundHashOperations.java:85)
        在org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.saveDelta(RedisOperationsSessionRepository.java:409)
        在org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.access$000(RedisOperationsSessionRepository.java:331)
        在org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:211)
        在org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:141)
        在org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:193)
        在org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:169)
        在org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:127)
        在org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:65)
        在org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
        在org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        在org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
        在org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        在org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
        在org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        在org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
        在org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        在org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
        在org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        在org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:68)
        在org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        在org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
        在org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        在org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
        在org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
        在org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
        在org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
        在org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
        在org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
        在org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
        在org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
        在org.apache.coyote.AbstractProtocol $ AbstractConnectionHandler.process(AbstractProtocol.java:668)
        在org.apache.tomcat.util.net.NioEndpoint $ SocketProcessor.doRun(NioEndpoint.java:1521)
        在org.apache.tomcat.util.net.NioEndpoint $ SocketProcessor.run(NioEndpoint.java:1478)
        在java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        在java.util.concurrent.ThreadPoolExecutor中的$ Worker.run(ThreadPoolExecutor.java:617)
        在org.apache.tomcat.util.threads.TaskThread $ WrappingRunnable.run(TaskThread.java:61)
        在java.lang.Thread.run(Thread.java:745)
org.springframework.core.serializer.support.SerializationFailedException:产生的原因无法使用DefaultSerializer序列化对象;嵌套例外是java.io.NotSerializableException:com.sun.jndi.ldap.LdapCtx
        在org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:67)
        在org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:34)
        在org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:50)
        ... 40共同框架省略
致:java.io.NotSerializableException:com.sun.jndi.ldap.LdapCtx
        在java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
        在java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
        在java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
        在java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
        在java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
        在java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
        在java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441)
        在java.lang.Throwable.writeObject(Throwable.java:985)
        在sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)
        在sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        在sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        在java.lang.reflect.Method.invoke(Method.java:497)
        在java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988)
        在java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
        在java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
        在java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
        在java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
        在org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:44)
        在org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:62)
        ... 42共同框架省略
 
使用spring data redis默认redisTemplate带来的序列化问题 数据库 u013905744的专栏 CSDN博客

解决方案

我想出了一个解决这个问题。我愿意接受任何建议,以改善答案。

该解决方案是不完整的,因为我需要看看专门为 com.sun.jndi.ldap.LdapCtx时,序列化失败,这样我就可以处理这个特定的情况下,并扔SerializationException中的所有其他类型。但我认为一般的想法可能是有用的人谁是阻止这一点。

现在,当无效凭据使用(如错误的用户名或密码不正确)的应用程序返回到登录页面,而不是吹起来:)

我添加了一些RedisConfiguration的RedisTemplate春季会议使用,以取代。

 进口com.gateway.utils.LdapFailAwareRedisObjectSerializer;

@组态
公共类RedisConfiguration {

  @主
  @豆
  公共RedisTemplate<字符串,ExpiringSession> redisTemplate(RedisConnectionFactory的connectionFactory){
    RedisTemplate<字符串,ExpiringSession>模板=新RedisTemplate<字符串,ExpiringSession>();

    template.setKeySerializer(新StringRedisSerializer());
    template.setHashKeySerializer(新StringRedisSerializer());
    template.setHashValueSerializer(新LdapFailAwareRedisObjectSerializer());

    template.setConnectionFactory(connectionFactory的);
    返回模板;
  }
}
 

下面是我实现RedisSerializer(LdapFailAwareRedisObjectSerializer的是从这里)

 公共类LdapFailAwareRedisObjectSerializer实现RedisSerializer<对象> {

  私人器<对象,byte []的>串行=新SerializingConverter();
  私人器< byte []的,对象>解串器=新DeserializingConverter();

  静态的最后一个字节[] EMPTY_ARRAY =新的字节[0];

  公共对象反序列化(byte []的字节){
    如果(的isEmpty(字节)){
      返回null;
    }

    尝试 {
      返回deserializer.convert(字节);
    }赶上(例外前){
      抛出新SerializationException(无法反序列化,前);
    }
  }

  公共byte []的序列化(Object对象){
    如果(对象== NULL){
      返回EMPTY_ARRAY;
    }

    尝试 {
      返回serializer.convert(对象);
    }赶上(例外前){
      返回EMPTY_ARRAY;
      // TODO这里添加逻辑,只返回EMPTY_ARRAY已知条件
      //否则抛出SerializationException
      //抛出新SerializationException(无法序列,前);
    }
  }

  私人布尔的isEmpty(byte []的数据){
    返程(数据== NULL || data.length == 0);
  }
}
 

Hi I am new to Spring and Java, I am trying to implement a Gateway authentication server as described in this tutorial https://spring.io/guides/tutorials/spring-security-and-angular-js/

I got everything working and then tried to implement the authentication against our company Ldap server. It works if I use a valid username and password. When I use invalid credentials the application errors.

I am not at work so I don't have the exact error, but it is returning an ldap error (com.sun.jndi.ldap.LdapCtx) and Redis is trying to serialize it.

Is there something I am missing in my configuration. From what I have read I think I should be looking for a way to wrap/extend the class and implement Serializable, but I am unsure of the least invasive way to do this with Spring Boot.

Any help is greatly appreciated.

Thanks,

Mike Kowalski

PS I have been working mostly in dynamic languages and frameworks until now (Javascript/Node, Php/Laravel)

Here are what I think are the relevant parts of the Security configuration:

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .formLogin()
      .defaultSuccessUrl("/")
      .loginPage("/login")
      .permitAll()
      .and()
      .logout()
      .logoutSuccessUrl("/logout")
      .permitAll();


    http
      .authorizeRequests()
        .antMatchers("/login").permitAll()
        .anyRequest().authenticated()
    .and()
      .csrf().csrfTokenRepository(csrfTokenRepository())
    .and()
      .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
  }

  @Override
   protected void configure(AuthenticationManagerBuilder authManagerBuilder)          throws Exception {
    authManagerBuilder
      .authenticationProvider(activeDirectoryLdapAuthenticationProvider())
        .userDetailsService(userDetailsService());
   }

   @Bean
   public AuthenticationManager authenticationManager() {
      return new ProviderManager(
        Arrays.asList(activeDirectoryLdapAuthenticationProvider())
      );
   }

   @Bean
   public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
  ActiveDirectoryLdapAuthenticationProvider provider = new     ActiveDirectoryLdapAuthenticationProvider(
    "XXX.XXX", "ldaps://XXX.XXX:636");
      provider.setConvertSubErrorCodesToExceptions(true);
      provider.setUseAuthenticationRequestCredentials(true);
      return provider;
   }

  private Filter csrfHeaderFilter() {
    return new OncePerRequestFilter() {
      @Override
      protected void doFilterInternal(
        HttpServletRequest request,
        HttpServletResponse response, 
        FilterChain filterChain
      ) throws ServletException, IOException 
      {
        CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
            .getName());
        if (csrf != null) {
          Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
          String token = csrf.getToken();
          if (cookie == null || token != null
              && !token.equals(cookie.getValue())) {
            cookie = new Cookie("XSRF-TOKEN", token);
            cookie.setPath("/");
            response.addCookie(cookie);
          }
        }
        filterChain.doFilter(request, response);
      }
    };
  }

  private CsrfTokenRepository csrfTokenRepository() {
    HttpSessionCsrfTokenRepository repository = new     HttpSessionCsrfTokenRepository();
    repository.setHeaderName("X-XSRF-TOKEN");
    return repository;
  }

}

Here is part of the error that using invalid credentials:

2015-09-24 15:07:30.564 DEBUG 6552 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /login at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2015-09-24 15:07:30.564 DEBUG 6552 --- [nio-8080-exec-3] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@44258b05
2015-09-24 15:07:30.564 DEBUG 6552 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /login at position 4 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
2015-09-24 15:07:30.564 DEBUG 6552 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /login at position 5 of 13 in additional filter chain; firing Filter: ''
2015-09-24 15:07:30.564 DEBUG 6552 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /login at position 6 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
2015-09-24 15:07:30.564 DEBUG 6552 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/login'; against '/logout'
2015-09-24 15:07:30.580 DEBUG 6552 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /login at position 7 of 13 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2015-09-24 15:07:30.580 DEBUG 6552 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/login'; against '/login'
2015-09-24 15:07:30.580 DEBUG 6552 --- [nio-8080-exec-3] w.a.UsernamePasswordAuthenticationFilter : Request is to process authentication
2015-09-24 15:07:30.580 DEBUG 6552 --- [nio-8080-exec-3] o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider
2015-09-24 15:07:30.580 DEBUG 6552 --- [nio-8080-exec-3] ctiveDirectoryLdapAuthenticationProvider : Processing authentication request for user: admin
2015-09-24 15:07:31.113 DEBUG 6552 --- [nio-8080-exec-3] ctiveDirectoryLdapAuthenticationProvider : Authentication for admin@countrycurtains.local failed:javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 525, vece]
2015-09-24 15:07:31.113  INFO 6552 --- [nio-8080-exec-3] ctiveDirectoryLdapAuthenticationProvider : Active Directory authentication failed: User was not found in directory
2015-09-24 15:07:31.114 DEBUG 6552 --- [nio-8080-exec-3] w.a.UsernamePasswordAuthenticationFilter : Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
2015-09-24 15:07:31.114 DEBUG 6552 --- [nio-8080-exec-3] w.a.UsernamePasswordAuthenticationFilter : Updated SecurityContextHolder to contain null Authentication
2015-09-24 15:07:31.114 DEBUG 6552 --- [nio-8080-exec-3] w.a.UsernamePasswordAuthenticationFilter : Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@28626d9a
2015-09-24 15:07:31.114 DEBUG 6552 --- [nio-8080-exec-3] .a.SimpleUrlAuthenticationFailureHandler : Redirecting to /login?error
2015-09-24 15:07:31.115 DEBUG 6552 --- [nio-8080-exec-3] o.s.s.web.DefaultRedirectStrategy        : Redirecting to '/login?error'
2015-09-24 15:07:31.115 DEBUG 6552 --- [nio-8080-exec-3] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2015-09-24 15:07:31.139 DEBUG 6552 --- [nio-8080-exec-3] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2015-09-24 15:07:31.148 ERROR 6552 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.io.NotSerializableException: com.sun.jndi.ldap.LdapCtx
        at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:52)
        at org.springframework.data.redis.core.AbstractOperations.rawHashValue(AbstractOperations.java:146)
        at org.springframework.data.redis.core.DefaultHashOperations.putAll(DefaultHashOperations.java:128)
        at org.springframework.data.redis.core.DefaultBoundHashOperations.putAll(DefaultBoundHashOperations.java:85)
        at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.saveDelta(RedisOperationsSessionRepository.java:409)
        at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.access$000(RedisOperationsSessionRepository.java:331)
        at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:211)
        at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:141)
        at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:193)
        at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:169)
        at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:127)
        at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:65)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:68)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.io.NotSerializableException: com.sun.jndi.ldap.LdapCtx
        at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:67)
        at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:34)
        at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:50)
        ... 40 common frames omitted
Caused by: java.io.NotSerializableException: com.sun.jndi.ldap.LdapCtx
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
        at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
        at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
        at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441)
        at java.lang.Throwable.writeObject(Throwable.java:985)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
        at org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:44)
        at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:62)
        ... 42 common frames omitted

解决方案

I figured out a solution to this problem. I am open to any suggestions to improve the answer.

The solution is not complete as I need to look specifically for the com.sun.jndi.ldap.LdapCtx type when serialization fails so I can deal with that specific case and throw the SerializationException in all others. But I thought the general idea might be useful to anyone who is blocked on this.

Now when invalid credentials are used (eg Bad Username or Incorrect Password) the application returns to the log in page rather than blowing up :)

I added some RedisConfiguration to replace the RedisTemplate Spring Session is using.

import com.gateway.utils.LdapFailAwareRedisObjectSerializer;

@Configuration
public class RedisConfiguration {

  @Primary
  @Bean
  public RedisTemplate<String,ExpiringSession> redisTemplate(RedisConnectionFactory connectionFactory) {
    RedisTemplate<String, ExpiringSession> template = new RedisTemplate<String, ExpiringSession>();

    template.setKeySerializer(new StringRedisSerializer());
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(new LdapFailAwareRedisObjectSerializer());

    template.setConnectionFactory(connectionFactory);
    return template;
  }
}

Here is my implementation of RedisSerializer (LdapFailAwareRedisObjectSerializer which is got from here)

public class LdapFailAwareRedisObjectSerializer implements RedisSerializer<Object> {

  private Converter<Object, byte[]> serializer = new SerializingConverter();
  private Converter<byte[], Object> deserializer = new DeserializingConverter();

  static final byte[] EMPTY_ARRAY = new byte[0];

  public Object deserialize(byte[] bytes) {
    if (isEmpty(bytes)) {
      return null;
    }

    try {
      return deserializer.convert(bytes);
    } catch (Exception ex) {
      throw new SerializationException("Cannot deserialize", ex);
    }
  }

  public byte[] serialize(Object object) {
    if (object == null) {
      return EMPTY_ARRAY;
    }

    try {
      return serializer.convert(object);
    } catch (Exception ex) {
      return EMPTY_ARRAY;
      //TODO add logic here to only return EMPTY_ARRAY for known conditions
      // else throw the SerializationException
      // throw new SerializationException("Cannot serialize", ex);
    }
  }

  private boolean isEmpty(byte[] data) {
    return (data == null || data.length == 0);
  }
}