作为Spring框架的初学者,我正在为我的公司从事一个使用LDAP身份验证的项目。对于开发,该代码使用“ ldif”(ApacheDS)测试身份验证。然后,已记录的用户详细信息(会话)将保存在CustomLdapUserDetails(实现LdapUserDetails)中。然后,在应用程序中的任何地方都使用CustomLdapUserDetails来检索会话信息。
问题
在进行开发时,每次我重新编译项目以查看更改如何反映在应用程序中时,该会话都会过期。发生这种情况是因为在每次重新启动时,Spring框架都会删除ApacheDS目录。
我希望能解决此问题,因为我无法一次又一次登录。我已经尝试通过添加以下内容来进行内存中身份验证:
authManagerBuilder.inMemoryAuthentication().withUser("user").password("test2").authorities("SUPER_ADMIN");
由于该项目使用CustomLdapUserDetails而不是UserDetails,所以收到转换错误:
org.springframework.security.core.userdetails.User cannot be cast to com.domain.configuration.datasource.CustomLdapUserDetails
任何帮助将不胜感激。
SecurityConfiguration.java
package com.domain.configuration;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
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.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.domain.configuration.datasource.CustomLdapUserMapper;
import com.domain.model.RoleType;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ComponentScan("com.domain")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@SuppressWarnings("unused")
private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
private static final String[] IGNORED_RESOURCE_LIST = new String[] {"/assets/**", "/static/**", "classpath:/resources/pdf_rendering/**", "/bdd/**"};
private static final String[] PERMITALL_RESOURCE_LIST = new String[] {"/json/**", "/errors/**", "/login", "/lostPassword"};
private static final String[] ADMIN_RESOURCE_LIST = new String[] {"/template/admin**"};
private static final String[] ADMIN_ROLES = new String[] {RoleType.SUPER_ADMIN.toString(), RoleType.CENTRAL_ADMIN.toString()};
@Value("${ldap.mocked.file}")
private String ldapMockedFile;
@Value("${ldap.url}")
private String ldapUrl;
@Value("${ldap.user.search.filter}")
private String ldapUserSearchFilter;
@Value("${ldap.user.search.base}")
private String ldapUserSearchBase;
@Value("${ldap.managerDn}")
private String managerDn;
@Value("${ldap.managerPasswd}")
private String managerPw;
@Value("${spring.profiles.active}")
private String profil;
@Autowired
private CustomLdapUserMapper customLdapMapper;
@Override
/**
* @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(WebSecurity)
*/
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(IGNORED_RESOURCE_LIST);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers(PERMITALL_RESOURCE_LIST).permitAll().antMatchers(ADMIN_RESOURCE_LIST)
.hasAnyRole(ADMIN_ROLES).anyRequest().authenticated();
http.formLogin().failureUrl("/login?error").defaultSuccessUrl("/home").loginPage("/login").defaultSuccessUrl("/home", true)
.permitAll().and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login")
.permitAll().and().exceptionHandling().accessDeniedPage("/403");
http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin();
}
@Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> provider = authManagerBuilder.ldapAuthentication()
.userDetailsContextMapper(this.customLdapMapper);
provider = provider.userSearchFilter(this.ldapUserSearchFilter);
provider.ldapAuthoritiesPopulator(generateMockLdapAuthoritiesPopulatorForAd());
if (this.ldapUserSearchBase != null && !this.ldapUserSearchBase.isEmpty()) {
provider = provider.userSearchBase(this.ldapUserSearchBase);
}
if ("dev".equals(this.profil) || "qualif".equals(this.profil)) {
provider.contextSource().ldif(this.ldapMockedFile);
} else {
provider.contextSource().managerDn(managerDn);
provider.contextSource().managerPassword(managerPw);
provider.contextSource().url(this.ldapUrl);
}
authManagerBuilder.inMemoryAuthentication().withUser("user").password("test2").authorities("SUPER_ADMIN");
}
/**
* Company's AD does not support basic ldap authorities, we handle them differently.
* @return
*/
private LdapAuthoritiesPopulator generateMockLdapAuthoritiesPopulatorForAd() {
return new LdapAuthoritiesPopulator() {
@Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData,
String username) {
// Roles are not handled by AD
return null;
}
};
}
}
CustomLdapUserDetails.java
package com.domain.configuration.datasource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.ldap.userdetails.LdapUserDetails;
import com.domain.dto.RoleDto;
public class CustomLdapUserDetails implements LdapUserDetails {
private static final long serialVersionUID = 1L;
private LdapUserDetails details;
private String firstname;
private String lastname;
private RoleDto roleDto;
private Locale locale;
private Long countryId;
public CustomLdapUserDetails(LdapUserDetails details) {
this.details = details;
}
/**
* @return the firstname
*/
public String getFirstname() {
return this.firstname;
}
/**
* @param firstname
* the firstname to set
*/
public void setFirstname(String firstname) {
this.firstname = firstname;
}
/**
* @return the lastname
*/
public String getLastname() {
return this.lastname;
}
/**
* @param lastname
* the lastname to set
*/
public void setLastname(String lastname) {
this.lastname = lastname;
}
/**
* @return the roleDto
*/
public RoleDto getRole() {
return this.roleDto;
}
/**
* @param roleDto
* the roleDto to set
*/
public void setRole(RoleDto roleDto) {
this.roleDto = roleDto;
}
/**
* @return the locale
*/
public Locale getLocale() {
return this.locale;
}
/**
* @param locale
* the locale to set
*/
public void setLocale(Locale locale) {
this.locale = locale;
}
@Override
public boolean isEnabled() {
return this.details.isEnabled();
}
@Override
public String getDn() {
return this.details.getDn();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(this.roleDto.getType().toString()));
return authorities;
}
@Override
public String getPassword() {
return this.details.getPassword();
}
@Override
public String getUsername() {
return this.details.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return this.details.isAccountNonExpired();
}
@Override
public boolean isAccountNonLocked() {
return this.details.isAccountNonLocked();
}
@Override
public boolean isCredentialsNonExpired() {
return this.details.isCredentialsNonExpired();
}
/**
* @return the countryId
*/
public Long getCountryId() {
return this.countryId;
}
/**
* @param countryId
* the countryId to set
*/
public void setCountryId(Long countryId) {
this.countryId = countryId;
}
}
CustomLdapUserMapper.java
package com.domain.configuration.datasource;
import java.util.Collection;
import org.apache.commons.lang3.LocaleUtils;
import org.parboiled.common.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.ldap.userdetails.LdapUserDetails;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import org.springframework.stereotype.Component;
import com.domain.dto.UserDto;
import com.domain.service.interfaces.UserService;
@Component
public class CustomLdapUserMapper extends LdapUserDetailsMapper {
private static final Logger logger = LoggerFactory.getLogger(CustomLdapUserMapper.class);
@Autowired
private UserService userService;
@Value("${ldap.ad.group.filter}")
private String groupFilter;
@Value("${ldap.ad.member.attribute}")
private String memberOf;
@Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
logger.debug("connection attempt from user: "+username);
boolean userIsFromGroup = false;
if (!StringUtils.isEmpty(groupFilter)) {
Object[] objectAttributes = ctx.getObjectAttributes(memberOf);
for (int i = 0; i < objectAttributes.length; i++) {
if(objectAttributes[i].toString().contains(groupFilter)) {
userIsFromGroup = true;
};
}
if (!userIsFromGroup) {
logger.debug("User "+username+" not found in the directory service.");
throw new UsernameNotFoundException(String.format("User with username=%s was not found", username));
}
}
final UserDto userInfo = this.userService.getUserByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(String.format("User with username=%s was not found", username)));
logger.info(String.format("Find role of User=%s as role_Id=%s", username, userInfo.getRole().getId()));
UserDetails details = super.mapUserFromContext(ctx, username, AuthorityUtils.createAuthorityList(userInfo.getRole().toString()));
CustomLdapUserDetails customLdapUser = new CustomLdapUserDetails((LdapUserDetails) details);
customLdapUser.setFirstname(userInfo.getFirstname());
customLdapUser.setLastname(userInfo.getLastname());
customLdapUser.setRole(userInfo.getRole());
customLdapUser.setLocale(LocaleUtils.toLocale(userInfo.getLocale().getCode()));
customLdapUser.setCountryId(userInfo.getRegion().getBusinessUnitDto().getCountryDto().getId());
return customLdapUser;
}
}