♣ Tech & Biz Salon/Internet, PC

Java에서 Active Directory 인증 구현 (Spring LDAP 이용)

TasteGod 2015. 6. 3. 21:17

Java에서 Active Directory 인증 구현 (Spring LDAP 이용)

Java Active Directory Authentication Sample using Spring LDAP


참고할만한 Sample 이나 정리된 자료가 마땅치 않더군요.

얼마 안되는 Sample도 버젼, 환경 등이 다르기도 하구요.

진행한 내용 정리해봅니다. 도움이 되었으면 좋겠네요.

# 환경

JDK 1.6

SPRING 3.2

spring ldap 2.0.3


* 현재 app의 경우 maven 같은 빌드툴이 적용되어 있지 않습니다.

  그래서 spring-ldap-core-2.0.3.RELEASE.jar 파일을 구글링후 maven 정보 사이트에서 받았네요.

* 이런 저런 방식으로 구현을 시도하면서 몇 개 파일을 더 받았는데.. 아래 sample 에서 필요없을수도 있습니다.

  혹시 해당 class 없다고 하면 구글링후 필요한 라이브러리를 다운로드 또는 maven 등을 통해 받으시구요.


spring context xml 설정

* context-ldap.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:ldap="http://www.springframework.org/schema/ldap"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
   
     
     <ldap:context-source
          url="LDAP://sex.com"
          base="DC=sex,DC=com"
          username="usernamehere"
          password="passwordhere" />
         
    <ldap:ldap-template id="ldapTemplate" />
   
   
    <!-- 

    This also works like above.
    <bean id="ldapTemplate">
        <property name="contextSource" ref="contextSource" />
    </bean>

    <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
        <property name="url" value="LDAP://sex.com" />
        <property name="base" value="DC=sex,DC=com" />
        <property name="username" value="usernamehere" />
        <property name="password" value="passwordhere" />
    </bean>
     -->
</beans>


Class referring to context xml

* TestAd.java

import org.apache.log4j.Logger;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.Filter;
import org.springframework.ldap.support.LdapUtils;

public class TestAd {

    private static Logger logger = Logger.getLogger(TestAd.class);

   
    public static void main(String[] args){
       
        logger.debug("start");
       
        boolean bl = auth_0603_09_40();
       
        logger.debug("bl :" + bl);
        logger.debug("end");
    }

   
    public static boolean auth_0603_09_40(){
       
        boolean blRet = false;
       
        String userEmpNo;
        String userPassword;
       
        userEmpNo = "Sex208126";
        userPassword = "difficultpassword";
        
        try{
            LdapContextSource contextSource = new LdapContextSource();
            contextSource.setUrl("LDAP://sex.com");
            contextSource.setBase("DC=sex,DC=com");
            contextSource.setUserDn("usernamehere");
            contextSource.setPassword("!passwordhere");
            contextSource.afterPropertiesSet();
             
            LdapTemplate ldapTemplate = new LdapTemplate(contextSource);
            try {
                ldapTemplate.afterPropertiesSet();
            } catch (Exception e) {
                e.printStackTrace();
            }
           
            logger.debug("ldapTemplate created ----------------------------");
           
            ldapTemplate.setIgnorePartialResultException(true) ;
           
            String base = "";
           
            String strFilter = "(&(objectClass=user)(objectCategory=person)(cn=" + userEmpNo + "))";
           
            logger.debug("filter created ----------------------------");
           
            blRet = ldapTemplate.authenticate(LdapUtils.emptyLdapName(), strFilter, userPassword);
           
            logger.debug("blRet:" + blRet );
   
        }
        catch(Exception e){
            e.printStackTrace();
        }
       
        return blRet;
    }   


Class without context xml

@Controller
@RequestMapping("/auth")
public class AuthController {
   
    @Autowired
    LdapTemplate ldapTemplate;
   
    private static Logger logger = Logger.getLogger(AuthController.class);

   
    @RequestMapping(value="/testad")
    public String doLoginAd(Model model, HttpServletRequest request, HttpServletResponse response, HttpSession session){
           
        boolean bl = auth_0603_09_50();
       
        logger.debug("bl :" + bl);
                        
        logger.debug("testad #############################");
           
        return "auth/testad";
    }


    private boolean auth_0603_09_50(){
       
        boolean blRet = false;
       
        String userEmpNo;
        String userPassword;
       
        userEmpNo = "Sex208126";
        userPassword = "difficultpassword";
       
        try{

            ldapTemplate.setIgnorePartialResultException(true) ;
           
            String strFilter = "(&(objectClass=user)(objectCategory=person)(cn=" + userEmpNo + "))";
           
            logger.debug("filter created ----------------------------");
           
            blRet = ldapTemplate.authenticate(LdapUtils.emptyLdapName(), strFilter, userPassword);
           
            logger.debug("blRet:" + blRet );
   
        }
        catch(Exception e){
            e.printStackTrace();
        }
       
        return blRet;
    }  


주요 사항 1 : setIgnorePartialResultException

▶ ldapTemplate.setIgnorePartialResultException(true) ; 로 아래의 Exception 해결org.springframework.ldap.PartialResultException: Unprocessed Continuation Reference(s); nested exception is javax.naming.PartialResultException: Unprocessed Continuation Reference(s); remaining name '/'
    at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:216)
    at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:385)
    at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:309)
    at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:642)
    at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:578)
    at org.springframework.ldap.core.LdapTemplate.authenticate(LdapTemplate.java:1441)
    at org.springframework.ldap.core.LdapTemplate.authenticate(LdapTemplate.java:1426)
    at org.springframework.ldap.core.LdapTemplate.authenticate(LdapTemplate.java:1359)
    at TestAd.auth_0603_09_40(TestAd.java:151)
    at TestAd.main(TestAd.java:65)
Caused by: javax.naming.PartialResultException: Unprocessed Continuation Reference(s); remaining name '/'
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source)
    at com.sun.jndi.ldap.LdapNamingEnumeration.getNextBatch(Unknown Source)
    at com.sun.jndi.ldap.LdapNamingEnumeration.hasMoreImpl(Unknown Source)
    at com.sun.jndi.ldap.LdapNamingEnumeration.hasMore(Unknown Source)
    at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:365)
    ... 8 more


주요 사항 2 : LdapUtils.emptyLdapName()

 ldapTemplate.authenticate 의 첫번째 파라미터에 LdapUtils.emptyLdapName() 세팅.

여기에 base 값을 넣었을때 에러 발생.

base 정보가 상기 코드보다 더 값들이 있어야 full 값이다. full 값을 넣어야한다는 얘기도 있는데..

고객한테 받은 정보가 위의 코드에 있는 값이 다라서... 더 문의하거나 하는것도 쉽게 답받을수 있는 과정도 아니라

지금의 방식으로 해보았는데 잘 되더군요.


마무리

authenticate 메쏘드가 다양한 파라미터로 수십개정도 있더군요. Exception을 만나면서 이런저런 방법을 시도해보았고

인증이 잘안되어서... search 를 통해서 인증처럼 사용할까 싶어 search 도 하고... 그러다가 시간이 많이 걸렸습니다.


참고로 filter 의 경우.. 예를 들면 아래와 같은 형태로도 가능할것으로 추측됩니다.

Filter filter = new EqualsFilter("sAMAccountName", "xxxxxxx");

ldapTemplate.authenticate(base, filter.encode(), ... )



글재주가 부족해서 포스팅에 시간이 많이 걸려요;; 기운 좀 불어넣어주시렵니까?

 아래  공감 버튼 눌러주시면 힘도 펄펄 나고 happy 해진답니다 ㅎㅎ  로그인 필요없어요^^