모기업에서의 AD연동을 Ldaps로 요청하여 작성하게 되었다.
추후에 Okta도 작성하겠지만, 최근 대기업에서의 Ldaps 비중은 점점 낮아지는 듯 한 느낌.
그래서 구글에서 찾아봐도 한국말로된 연동관련 포스팅은 찾아보기가 힘들다..
.....
우선 Ldaps가 무엇인지 알아야하는데,
AD ( Active Directory ) 를 사용하는 일종의 사용자관리 서비스 생각하면 편할듯하다.
ldap / ldaps ?
ldap은 http 통신, ldaps는 ssl인증을 통한 https 통신이라고 생각하자. ( 인증서 필요 )
pom.xml에 따로 security-ldap 을 추가하는 경우도 있는데, 사실 이번 프로젝트의 경우 ldaps의 인증값으로 stragy 전략을
짜는게 아니라, 내부적으로 존재하는 user의 oauth2 stragy를 사용하기때문에, 따로 지정은 해주지 않았다.
fun login(idUser: String, passUser: String): LdapContext {
val env= Hashtable<String, String>()
val keystorePath = /* 키스토어 위치 */
val keystorePW = /* 키스토어 비밀번호 */
val url = /* Ldap 요청 URL */
val ldapContext:InitialLdapContext
val loginFormat = /* ldaps에 요청할 로그인 형식 */
try {
System.setProperty("javax.net.ssl.keyStore", keystorePath)
System.setProperty("javax.net.ssl.trustStore", keystorePath)
System.setProperty("javax.net.ssl.keyStorePassword", keystorePW)
System.setProperty("javax.net.ssl.trustStorePassword", keystorePW)
/* 밑에이있는 부분은 ldaps 설정에 맞춰주시면 됩니다 */
env[Context.INITIAL_CONTEXT_FACTORY] = "com.sun.jndi.ldap.LdapCtxFactory"
env[Context.SECURITY_AUTHENTICATION] = "simple"
env[Context.PROVIDER_URL] = url
//밑 PartialResultException 참조
//Performance issue
//if not assigned to ignore code,
//getting partialResultException when running scheduler
//OU 값을 추가하거나 해야하는데, 그럴 수 없는 경우에는 사용해야 할 듯 싶다 .
//( 또는 포트변경이 있지만, 대부분의 경우에서 그럴 일은 없을 듯 )
//env[Context.REFERRAL] = "follow"
logger.info("LDAP(login) : Login Account id : $loginFormat")
env[Context.SECURITY_PRINCIPAL] = loginFormat
env[Context.SECURITY_CREDENTIALS] = passUser
ldapContext = InitialLdapContext(env, null)
logger.info("LDAP(login) : Successfully connected.")
}catch (e: AuthenticationException){
logger.info("LDAP : AuthenticationException")
throw ErrorResp.getBadRequest().apply {
addError("idUser", idUser, getMessage("exception.bad_credentials"))
}
}catch (e: NamingException) {
//말그대로 Name 에 관한 Exception인데, 조금 혼동을 주는 부분이 있다.
//아이디 dn 이 잘못된경우도 이쪽으로 잡히는것 때문인데,
//사실 위쪽의 AuthenticationException 잡혔어야 하는부분이지만,
//AuthenticationException은 정말 인증에대해 모종의 이유로 실패한 경우이고,
//별칭이 잘못되어서 이쪽으로 잡히는 경우도 분명 있다.
//이 외에도 namingException에 걸리는 이유로는 다음의 이유들이 있다.
//Operations error, Alias problem,
//Alias dereferencing problem, Loop detected, Affects multiple DSAs
//현재로썬, url이 다르거나 dn 별칭이 잘못된 경우이다.
logger.info("LDAP : NamingException 입력값 확인 또는 Service Account 값 확인")
throw ErrorResp.getBadRequest().apply {
addError("idUser or ldap url", "$idUser , $url", getMessage("exception.bad_credentials"))
}
}catch(e: NameNotFoundException) {
logger.error("LDAP : NameNotFoundException")
//search DN이 잘못된 부분
throw ErrorResp.getBadRequest().apply {
addError("filter search", "search DN", getMessage("exception.bad_credentials"))
}
}catch(e: CommunicationException) {
//Protocol error 말그대로 통신에러.
//특징이 있다면 namingException에 동기화 및 직렬화 관련한 문제는 이쪽으로 잡힌다.
logger.info("CommunicationException")
throw ErrorResp.getInternalServerError().apply {
addError("keystorePath", keystorePath, getMessage("exception.unauthorized"))
}
}catch(e: PartialResultException) {
//기본 DN을 기반으로 LDAP 트리에 액세스 할 수 있는 바인드 DN이 아닌경우
//해결 방법으로는 위에 주석친 referral 을 follow 하거나,
//( search 연속 참조이지만, 성능이슈 있음 )
//포트변경하거나 , ( 389 -> 3268 , 636 -> 3269 )
//액세스 할 수 있는 OU의 값이 필요하다.
//셋 중 하나라도 바꾼다면 각각의 이슈가 분명 있을거기 때문에 유저의 경우
//ldap objectClass가 잘 정의되어있어야 한다.
//여기에 만약 걸린다면, test용 계정으로 search tree에 잡히지 않는 경우.
logger.info("PartialResultException")
throw ErrorResp.getInternalServerError().apply {
addError("idUser", idUser, getMessage("exception.bad_credentials"))
}
}
return ldapContext
}
다른것은 사실 초기세팅이고 ( keystore에 인증서를 등록한다거나, test 해볼 ldap을 설정한다거나 )
바로 로그인하고, 로그인한 사용자의 정보를 받는 코드부터 보자면
쫌 길어보이기만, 반이상이 Exception 관련 코드이다, 뭐만하면 다른 에러로 고생시켜가지고 몇가지 분리해보았는데,
대표적으로는 5가지 오류가 있는듯 하다. 주석참고
참고용이긴한데.. 아마 ldaps 연동을 원하는 회사의 경우 cert file을 하나 건네줄텐대, 자바 keystore에 등록하게 된다면
아마 default 경로가 mac의 경우
/Library/Java/JavaVirtualMachines/[jdk버전]/Contents/Home/jre/lib/security/cacerts
일거고, password도 default로 'changeit' 일것이다.
이것도 참고용인데.. ldap 테스트용으로 구축하려면 docker를 강력히 추천하는바..
( 6개월전에 진행했던 내용이라서 6months ago가 적혀있네요.. )
openldap 이라는 훌륭한 녀석이 있어서 아주아주 손쉽게 구축이 가능하다.
2019/06/27 - [프로그래밍 공부/Etc] - [Docker] boot를 Docker로 실행해보자
이건 윈도우 pc일때 사용했던건데, mac도 크게 다르지않다.
https://github.com/osixia/docker-openldap
osixia에서 만든 docker용 openldap. 다운로드및 내용은 아주 자세하게 설명이되어있어, 따라하기 어렵지않다.
이것도 참고용인데, 당연히 Ldaps또한 GUI 툴이 존재한다
테스트로 사용했던 GUI툴은
Apache의 ApacheDirectoryStudio이다.
https://directory.apache.org/studio/
1. Ldap 서버를 띄운다 ( ldaps 의 경우 :636 포트이니 유의 )
2. 인증서 생성 또는 등록 등 인증서 관련 문제 해결
3. directory studio로 접근이 되는지 확인
4. spring으로 로그인 구현하기
'프로그래밍 공부 > Spring Boot' 카테고리의 다른 글
[Slack] slack bot message (0) | 2022.10.19 |
---|---|
[java] spring boot oauth2 refresh token 고민 (0) | 2022.07.13 |
[Kotlin] java POI 를 이용한 write / download (0) | 2022.03.08 |
[Java] spring boot - firebase message server (11) | 2021.07.27 |
[GraphQL] Spring Boot + 그래프QL 사용하기 (CRUD) (4) | 2019.07.08 |