[英]Spring-security-ldap: Failed instantiate InitialContextFactory com.sun.jndi.ldap.LdapCtxFactory
[英]Spring Security and LDAP - java.io.NotSerializableException: com.sun.jndi.ldap.LdapCtx
我已经通过ActiveDirectoryLdapAuthenticationProvider
将我的应用程序与LDAP集成在一起。 当我使用正确的凭据登录时,一切正常。 使用错误的凭据会出现问题:
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:68)
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:35)
at org.springframework.session.data.mongo.JdkMongoSessionConverter.serializeAttributes(JdkMongoSessionConverter.java:105)
at org.springframework.session.data.mongo.JdkMongoSessionConverter.convert(JdkMongoSessionConverter.java:84)
at org.springframework.session.data.mongo.AbstractMongoSessionConverter.convert(AbstractMongoSessionConverter.java:110)
at org.springframework.session.data.mongo.MongoOperationsSessionRepository.convertToDBObject(MongoOperationsSessionRepository.java:141)
at org.springframework.session.data.mongo.MongoOperationsSessionRepository.save(MongoOperationsSessionRepository.java:77)
at org.springframework.session.data.mongo.MongoOperationsSessionRepository.save(MongoOperationsSessionRepository.java:44)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:245)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:217)
at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:170)
at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:80)
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:197)
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.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
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:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:677)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
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: 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:498)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1028)
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.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:498)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1028)
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 java.util.HashMap.internalWriteEntries(HashMap.java:1777)
at java.util.HashMap.writeObject(HashMap.java:1354)
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:498)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1028)
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:46)
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:63)
... 38 common frames omitted
而且我收到500而不是登录页面上的消息。 登录页面是由Spring Security生成的。
我使用@EnableMongoHttpSession
。 JdkMongoSessionConverter
用作AbstractMongoSessionConverter
实现。
我知道问题在于序列化LdapCtx
但是我该如何处理呢? 我的意思是简单的捕获异常,并在登录页面上显示诸如“凭据错误”之类的消息。
Spring Security配置:
package xxx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${spring.security.role.name}")
private String roleName;
@Autowired
private AuthenticationProvider provider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/app/**").fullyAuthenticated()
.antMatchers("/operations/**").hasAuthority(roleName)
.anyRequest().permitAll()
.and()
.formLogin()
.loginProcessingUrl("/login");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider);
}
@Bean
@Profile("dev-tests")
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(devUserDetailsService());
ShaPasswordEncoder passwordEncoder = new ShaPasswordEncoder(1);
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
}
@Bean
@ConditionalOnMissingBean(AuthenticationProvider.class)
public AuthenticationProvider ldapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(
"****",
"ldaps://****:3269",
"OU=****,DC=****,DC=****,DC=****"
);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setSearchFilter("(&(objectClass=userProxy)(userPrincipalName={0}))");
return provider;
}
private UserDetailsService devUserDetailsService() {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(roleName));
authorities.add(new SimpleGrantedAuthority("ROLE_ACTUATOR"));
Collection<UserDetails> users = new ArrayList<>();
users.add(new User("admin", "d033e22ae348aeb5660fc2140aec35850c4da997", authorities)); //pass: admin
users.add(new User("read", "a7afddb68260a60f86c02a021efba7f216c2e7cf", new ArrayList<>())); //pass: read
return new InMemoryUserDetailsManager(users);
}
}
Mongo配置:
package xxxxx;
import xxxxx.SpringDataEnvServiceVersionRepository;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.session.data.mongo.JdkMongoSessionConverter;
import org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static java.time.ZoneOffset.UTC;
import static java.time.ZonedDateTime.ofInstant;
@Configuration
@EnableMongoRepositories(
basePackages = {"****"},
basePackageClasses = {SpringDataEnvServiceVersionRepository.class})
@EnableMongoHttpSession(collectionName = "****")
public class MongoConfiguration extends AbstractMongoConfiguration {
@Value("${spring.data.mongodb.uri}")
private String uri;
@Override
protected String getDatabaseName() {
return new MongoClientURI(uri).getDatabase();
}
@Bean
@Override
public Mongo mongo() {
return new MongoClient(new MongoClientURI(uri));
}
@Bean
public JdkMongoSessionConverter jdkMongoSessionConverter() {
return new JdkMongoSessionConverter();
}
@Bean
@Override
public CustomConversions customConversions() {
List<Converter> list = new ArrayList<>();
list.add(new DateToZonedDateTimeConverter());
list.add(new ZonedDateTimeToDateConverter());
list.add(new LocalDateToDateConverter());
list.add(new DateToLocalDateConverter());
list.add(new StringToZonedDateTimeConverter());
return new CustomConversions(list);
}
private static class DateToZonedDateTimeConverter implements Converter<Date, ZonedDateTime> {
@Override
public ZonedDateTime convert(Date source) {
return source == null ? null : ofInstant(source.toInstant(), UTC);
}
}
private static class ZonedDateTimeToDateConverter implements Converter<ZonedDateTime, Date> {
@Override
public Date convert(ZonedDateTime source) {
return source == null ? null : Date.from(source.toInstant());
}
}
private static class LocalDateToDateConverter implements Converter<LocalDate, Date> {
@Override
public Date convert(LocalDate source) {
return Date.from(source.atStartOfDay(UTC).toInstant());
}
}
private static class DateToLocalDateConverter implements Converter<Date, LocalDate> {
@Override
public LocalDate convert(Date source) {
return Instant.ofEpochMilli(source.getTime()).atZone(UTC).toLocalDate();
}
}
private static class StringToZonedDateTimeConverter implements Converter<String, ZonedDateTime> {
@Override
public ZonedDateTime convert(String s) {
return s == null ? null : ZonedDateTime.parse(s);
}
}
}
解决方案放在Spring Security配置中,如下代码:
@Bean
@ConditionalOnMissingBean(AuthenticationProvider.class)
public AuthenticationProvider ldapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(
"****",
"ldaps://****:3269",
"OU=****,DC=****,DC=****,DC=****"
);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setSearchFilter("(&(objectClass=userProxy)(userPrincipalName={0}))");
return new AuthenticationProvider() {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
try {
return provider.authenticate(authentication);
} catch (BadCredentialsException e) {
throw new BadCredentialsException(e.getMessage());
}
}
@Override
public boolean supports(Class<?> aClass) {
return provider.supports(aClass);
}
};
}
这样可以从异常原因中删除LdapCtx
实例,然后不必进行序列化。 如有任何建议,请通知我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.