[SPRING] 봄 보안 LDAP 및 기억하기
SPRING봄 보안 LDAP 및 기억하기
나는 LDAP와의 통합을 가지고있는 Spring Boot로 앱을 만들고있다. LDAP 서버에 성공적으로 연결하고 사용자를 인증 할 수있었습니다. 이제는 remember-me 기능을 추가해야합니다. 나는 다른 게시물 (이)을 보려고했지만 내 문제에 대한 답을 찾을 수 없었습니다. 공식적인 스프링 보안 문서에는
remember-me 기능을 추가하는 몇 가지 초기 생각을 가진 작업 코드는 다음과 같습니다.
WebSecurityConfig
import com.ui.security.CustomUserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.event.LoggerListener;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
String DOMAIN = "ldap-server.com";
String URL = "ldap://ds.ldap-server.com:389";
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/ui/**").authenticated()
.antMatchers("/", "/home", "/UIDL/**", "/ui/**").permitAll()
.anyRequest().authenticated()
;
http
.formLogin()
.loginPage("/login").failureUrl("/login?error=true").permitAll()
.and().logout().permitAll()
;
// Not sure how to implement this
http.rememberMe().rememberMeServices(rememberMeServices()).key("password");
}
@Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder
.authenticationProvider(activeDirectoryLdapAuthenticationProvider())
.userDetailsService(userDetailsService())
;
}
@Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(DOMAIN, URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setUserDetailsContextMapper(userDetailsContextMapper());
return provider;
}
@Bean
public UserDetailsContextMapper userDetailsContextMapper() {
UserDetailsContextMapper contextMapper = new CustomUserDetailsServiceImpl();
return contextMapper;
}
/**
* Impl of remember me service
* @return
*/
@Bean
public RememberMeServices rememberMeServices() {
// TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("password", userService);
// rememberMeServices.setCookieName("cookieName");
// rememberMeServices.setParameter("rememberMe");
return rememberMeServices;
}
@Bean
public LoggerListener loggerListener() {
return new LoggerListener();
}
}
사용자 정의 UserDetailsService Impl
public class CustomUserDetailsServiceImpl implements UserDetailsContextMapper {
@Autowired
SecurityHelper securityHelper;
Log ___log = LogFactory.getLog(this.getClass());
@Override
public LoggedInUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> grantedAuthorities) {
LoggedInUserDetails userDetails = null;
try {
userDetails = securityHelper.authenticateUser(ctx, username, grantedAuthorities);
} catch (NamingException e) {
e.printStackTrace();
}
return userDetails;
}
@Override
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
}
}
어떻게 든 UserService를 구현해야하지만 어떻게 달성 할 수 있는지 확신 할 수 없다는 것을 알고 있습니다.
해결법
-
==============================
1.LDAP를 통한 RememberMe 기능의 구성에는 두 가지 문제가 있습니다.
LDAP를 통한 RememberMe 기능의 구성에는 두 가지 문제가 있습니다.
나는이 단계별로 조치를 취할 것이다.
토큰 기반 기억 기능 (TokenBasedRememberMeServices)은 인증 과정에서 다음과 같은 방식으로 작동합니다.
사용자가 서비스로 돌아가서 Remember Me 기능을 사용하여 인증 받기를 원할 때 :
해시 검사 프로세스는 아무도 "가짜"기억 쿠키를 만들 수 없도록하여 다른 사용자를 가장 할 수있게하기 위해 필요합니다. 문제는이 프로세스가 저장소에서 비밀번호를로드 할 가능성에 의존한다는 것입니다.하지만 Active Directory에서는 불가능합니다. 사용자 이름을 기반으로 일반 텍스트 비밀번호를로드 할 수 없습니다.
이것은 토큰 기반 구현을 AD 사용에 부적합하게 만든다. (패스워드 나 다른 비밀 사용자 기반의 자격 증명을 포함하는 로컬 사용자 저장소를 만들기 시작하지 않는 한, 나는이 다른 접근법을 알지 못한다. 귀하의 응용 프로그램, 비록 그것은 좋은 방법일지도 모릅니다).
다른 하나는 영구적 인 토큰 (PersistentTokenBasedRememberMeServices)을 기반으로 구현되었으며 다음과 같이 간단하게 작동합니다.
사용자가 인증을 원할 때 :
보시다시피 암호는 더 이상 필요하지 않지만 암호 확인 대신 사용되는 토큰 저장소 (일반적으로 데이터베이스, 테스트를 위해 메모리에 사용할 수 있음)가 필요합니다.
그러면 구성 부분으로 넘어갑니다. 영구 토큰 기반 기억을위한 기본 구성은 다음과 같습니다.
@Override protected void configure(HttpSecurity http) throws Exception { .... String internalSecretKey = "internalSecretKey"; http.rememberMe().rememberMeServices(rememberMeServices(internalSecretKey)).key(internalSecretKey); } @Bean public RememberMeServices rememberMeServices(String internalSecretKey) { BasicRememberMeUserDetailsService rememberMeUserDetailsService = new BasicRememberMeUserDetailsService(); InMemoryTokenRepositoryImpl rememberMeTokenRepository = new InMemoryTokenRepositoryImpl(); PersistentTokenBasedRememberMeServices services = new PersistentTokenBasedRememberMeServices(staticKey, rememberMeUserDetailsService, rememberMeTokenRepository); services.setAlwaysRemember(true); return services; }
이 구현은 생산을 위해 JdbcTokenRepositoryImpl로 대체되어야하는 메모리 내 토큰 저장소를 사용합니다. 제공된 UserDetailsService는 Remember Me 쿠키에서로드 된 사용자 ID로 식별되는 사용자에 대한 추가 데이터를로드합니다. 가장 간단한 구현은 다음과 같습니다.
public class BasicRememberMeUserDetailsService implements UserDetailsService { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return new User(username, "", Collections.<GrantedAuthority>emptyList()); } }
필요에 따라 AD 또는 내부 데이터베이스에서 추가 속성이나 그룹 구성원을로드하는 다른 UserDetailsService 구현을 제공 할 수도 있습니다. 다음과 같이 보일 수 있습니다.
@Bean public RememberMeServices rememberMeServices(String internalSecretKey) { LdapContextSource ldapContext = getLdapContext(); String searchBase = "OU=Users,DC=test,DC=company,DC=com"; String searchFilter = "(&(objectClass=user)(sAMAccountName={0}))"; FilterBasedLdapUserSearch search = new FilterBasedLdapUserSearch(searchBase, searchFilter, ldapContext); search.setSearchSubtree(true); LdapUserDetailsService rememberMeUserDetailsService = new LdapUserDetailsService(search); rememberMeUserDetailsService.setUserDetailsMapper(new CustomUserDetailsServiceImpl()); InMemoryTokenRepositoryImpl rememberMeTokenRepository = new InMemoryTokenRepositoryImpl(); PersistentTokenBasedRememberMeServices services = new PersistentTokenBasedRememberMeServices(internalSecretKey, rememberMeUserDetailsService, rememberMeTokenRepository); services.setAlwaysRemember(true); return services; } @Bean public LdapContextSource getLdapContext() { LdapContextSource source = new LdapContextSource(); source.setUserDn("user@"+DOMAIN); source.setPassword("password"); source.setUrl(URL); return source; }
이렇게하면 LDAP와 함께 작동하고 SecurityContextHolder.getContext (). getAuthentication ()에서 사용할 수있는 RememberMeAuthenticationToken 내에로드 된 데이터를 제공하는 기능을 기억하게됩니다. 또한 LDAP 데이터를 사용자 개체 (CustomUserDetailsServiceImpl)로 구문 분석하기 위해 기존 논리를 다시 사용할 수 있습니다.
별도의 주제로, 질문에 게시 된 코드에 한 가지 문제가 있습니다. 다음을 대체해야합니다.
authManagerBuilder .authenticationProvider(activeDirectoryLdapAuthenticationProvider()) .userDetailsService(userDetailsService()) ;
와:
authManagerBuilder .authenticationProvider(activeDirectoryLdapAuthenticationProvider()) ;
userDetailsService에 대한 호출은 DAO 기반 인증을 추가하기 위해 (예 : 데이터베이스에 대해) 수행되어야하며 사용자 세부 정보 서비스의 실제 구현으로 호출해야합니다. 현재 구성으로 인해 무한 루프가 발생할 수 있습니다.
-
==============================
2.RememberMeService에 대한 참조가 필요한 UserService의 인스턴스가 누락 된 것 같습니다. LDAP를 사용하고 있기 때문에 LDAP 버전의 UserService가 필요합니다. JDBC / JPA 구현에만 익숙하지만, org.springframework.security.ldap.userdetails.LdapUserDetailsManager는 사용자가 찾고있는 것입니다. 그렇다면 설정은 다음과 같습니다.
RememberMeService에 대한 참조가 필요한 UserService의 인스턴스가 누락 된 것 같습니다. LDAP를 사용하고 있기 때문에 LDAP 버전의 UserService가 필요합니다. JDBC / JPA 구현에만 익숙하지만, org.springframework.security.ldap.userdetails.LdapUserDetailsManager는 사용자가 찾고있는 것입니다. 그렇다면 설정은 다음과 같습니다.
@Bean public UserDetailsService getUserDetailsService() { return new LdapUserDetailsManager(); // TODO give it whatever constructor params it needs } @Bean public RememberMeServices rememberMeServices() { TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("password", getUserDetailsService()); rememberMeServices.setCookieName("cookieName"); rememberMeServices.setParameter("rememberMe"); return rememberMeServices; }
from https://stackoverflow.com/questions/24745528/spring-security-ldap-and-remember-me by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] Spring 애플리케이션을위한 jboss-deployment-structure.xml이 필요한 이유는 무엇입니까? (0) | 2019.03.05 |
---|---|
[SPRING] Spring은이 종속성에 대한 autowire 후보가 될 수있는 적어도 하나의 bean을 예상했다. (0) | 2019.03.05 |
[SPRING] 스프링 CrudRepository 예외 (0) | 2019.03.05 |
[SPRING] 두 개의 MVC 구성으로 봄 부팅 (0) | 2019.03.05 |
[SPRING] Spring 3 / Hibernate에서 트랜잭션을 롤백하기위한 베스트 프랙티스 (0) | 2019.03.05 |