'2009/09/01'에 해당되는 글 2건

  1. 2009.09.01 Spring Security 와 Active Directory 연동 -2
  2. 2009.09.01 Spring Security 와 Active Directory 연동
인터넷을 뒤지다 보니 Spring Security와 Active Directory를 연동하는 또 다른 방식이 있다.

이 방식은 Customize된 Authentication Class를 생성해야 하는 단점이 있다.

하지만 반대로 우리와 같이 LDAP 연동시 Authorization 관련 로직을 Customize 할 때 쉬울 듯 하다.( 뭐 아직은 해보지 않은 상태 지만 내부 코드를 보면 Role 명을 쉽게 입력할 수 있을 것 같다. )

그리고 인증 방식은 LDAP 형태가 아닌 Computer Name\사용자 계정 형태이다.
( 이 부분이 AD가 아닌 Window 내부 사용자 계정 관리 방식으로 보여 조금 꺼림직 하다. )

하지만 관리자 계정 정보를 필요로 하지 않는 다는 엄청난(?) 장점이 있다.

출처 : http://stackoverflow.com/questions/84680/how-do-you-authenticate-against-an-active-directory-server-using-spring-security 첫번째 댓글

[Spring Security Config]
<beans:bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
       
<beans:constructor-arg value="ldap://hostname.queso.com:389/" />
</beans:bean>

<beans:bean id="ldapAuthenticationProvider" class="org.queso.ad.service.authentication.LdapAuthenticationProvider">
       
<beans:property name="authenticator" ref="ldapAuthenticator" />
       
<custom-authentication-provider />
</beans:bean>

<beans:bean id="ldapAuthenticator" class="org.queso.ad.service.authentication.LdapAuthenticatorImpl">
       
<beans:property name="contextFactory" ref="contextSource" />
       
<beans:property name="principalPrefix" value="QUESO\" />
</beans:bean>

위 내용 중 principalPrefix는 Computuer 명 을 넣으면 된다.
즉 인증시 "컴퓨터명\사용자ID"가 LDAP 서버로 전달된다.

[LdapAuthenticationProvider.java]
/**
 * Custom Spring Security authentication provider which tries to bind to an LDAP server with
 * the passed-in credentials; of note, when used with the custom {@link LdapAuthenticatorImpl},
 * does <strong>not</strong> require an LDAP username and password for initial binding.
 *
 * @author Jason
 */
public class LdapAuthenticationProvider implements AuthenticationProvider {
private LdapAuthenticator authenticator;

       
public Authentication authenticate(Authentication auth) throws AuthenticationException {

               
// Authenticate, using the passed-in credentials.
               
DirContextOperations authAdapter = authenticator.authenticate(auth);

               
// Creating an LdapAuthenticationToken (rather than using the existing Authentication
               
// object) allows us to add the already-created LDAP context for our app to use later.
               
LdapAuthenticationToken ldapAuth = new LdapAuthenticationToken(auth, "ROLE_USER");
               
InitialLdapContext ldapContext = (InitialLdapContext) authAdapter
                               
.getObjectAttribute("ldapContext");
               
if (ldapContext != null) {
                        ldapAuth
.setContext(ldapContext);
               
}

               
return ldapAuth;
       
}

       
public boolean supports(Class clazz) {
               
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(clazz));
       
}

       
public LdapAuthenticator getAuthenticator() {
               
return authenticator;
       
}

       
public void setAuthenticator(LdapAuthenticator authenticator) {
               
this.authenticator = authenticator;
       
}
}
[LdapAuthenticatorImpl.java]
/**
 * Custom Spring Security LDAP authenticator which tries to bind to an LDAP server using the
 * passed-in credentials; does <strong>not</strong> require "master" credentials for an
 * initial bind prior to searching for the passed-in username.
 *
 * @author Jason
 */

public class LdapAuthenticatorImpl implements LdapAuthenticator {

       
private DefaultSpringSecurityContextSource contextFactory;
       
private String principalPrefix = "";

       
public DirContextOperations authenticate(Authentication authentication) {

               
// Grab the username and password out of the authentication object.
               
String principal = principalPrefix + authentication.getName();
               
String password = "";
               
if (authentication.getCredentials() != null) {
                        password
= authentication.getCredentials().toString();
               
}

               
// If we have a valid username and password, try to authenticate.
               
if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
                       
InitialLdapContext ldapContext = (InitialLdapContext) contextFactory
                                       
.getReadWriteContext(principal, password);

                       
// We need to pass the context back out, so that the auth provider can add it to the
                       
// Authentication object.
                       
DirContextOperations authAdapter = new DirContextAdapter();
                        authAdapter
.addAttributeValue("ldapContext", ldapContext);

                       
return authAdapter;
               
} else {
                       
throw new BadCredentialsException("Blank username and/or password!");
               
}
       
}

       
/**
         * Since the InitialLdapContext that's stored as a property of an LdapAuthenticationToken is
         * transient (because it isn't Serializable), we need some way to recreate the
         * InitialLdapContext if it's null (e.g., if the LdapAuthenticationToken has been serialized
         * and deserialized). This is that mechanism.
         *
         * @param authenticator
         *          the LdapAuthenticator instance from your application's context
         * @param auth
         *          the LdapAuthenticationToken in which to recreate the InitialLdapContext
         * @return
         */

       
static public InitialLdapContext recreateLdapContext(LdapAuthenticator authenticator,
                       
LdapAuthenticationToken auth) {
               
DirContextOperations authAdapter = authenticator.authenticate(auth);
               
InitialLdapContext context = (InitialLdapContext) authAdapter
                               
.getObjectAttribute("ldapContext");
                auth
.setContext(context);
               
return context;
       
}

       
public DefaultSpringSecurityContextSource getContextFactory() {
               
return contextFactory;
       
}

       
/**
         * Set the context factory to use for generating a new LDAP context.
         *
         * @param contextFactory
         */

       
public void setContextFactory(DefaultSpringSecurityContextSource contextFactory) {
               
this.contextFactory = contextFactory;
       
}

       
public String getPrincipalPrefix() {
               
return principalPrefix;
       
}

       
/**
         * Set the string to be prepended to all principal names prior to attempting authentication
         * against the LDAP server.  (For example, if the Active Directory wants the domain-name-plus
         * backslash prepended, use this.)
         *
         * @param principalPrefix
         */

       
public void setPrincipalPrefix(String principalPrefix) {
               
if (principalPrefix != null) {
                       
this.principalPrefix = principalPrefix;
               
} else {
                       
this.principalPrefix = "";
               
}
       
}

}

[LdapAuthenticationToken.java]
/**
 * <p>
 * Authentication token to use when an app needs further access to the LDAP context used to
 * authenticate the user.
 * </p>
 *
 * <p>
 * When this is the Authentication object stored in the Spring Security context, an application
 * can retrieve the current LDAP context thusly:
 * </p>
 *
 * <pre>
 * LdapAuthenticationToken ldapAuth = (LdapAuthenticationToken) SecurityContextHolder
 *              .getContext().getAuthentication();
 * InitialLdapContext ldapContext = ldapAuth.getContext();
 * </pre>
 *
 * @author Jason
 *
 */

public class LdapAuthenticationToken extends AbstractAuthenticationToken {

       
private static final long serialVersionUID = -5040340622950665401L;

       
private Authentication auth;
       
transient private InitialLdapContext context;
       
private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

       
/**
         * Construct a new LdapAuthenticationToken, using an existing Authentication object and
         * granting all users a default authority.
         *
         * @param auth
         * @param defaultAuthority
         */

       
public LdapAuthenticationToken(Authentication auth, GrantedAuthority defaultAuthority) {
               
this.auth = auth;
               
if (auth.getAuthorities() != null) {
                       
this.authorities.addAll(Arrays.asList(auth.getAuthorities()));
               
}
               
if (defaultAuthority != null) {
                       
this.authorities.add(defaultAuthority);
               
}
               
super.setAuthenticated(true);
       
}

       
/**
         * Construct a new LdapAuthenticationToken, using an existing Authentication object and
         * granting all users a default authority.
         *
         * @param auth
         * @param defaultAuthority
         */

       
public LdapAuthenticationToken(Authentication auth, String defaultAuthority) {
               
this(auth, new GrantedAuthorityImpl(defaultAuthority));
       
}

       
public GrantedAuthority[] getAuthorities() {
               
GrantedAuthority[] authoritiesArray = this.authorities.toArray(new GrantedAuthority[0]);
               
return authoritiesArray;
       
}

       
public void addAuthority(GrantedAuthority authority) {
               
this.authorities.add(authority);
       
}

       
public Object getCredentials() {
               
return auth.getCredentials();
       
}

       
public Object getPrincipal() {
               
return auth.getPrincipal();
       
}

       
/**
         * Retrieve the LDAP context attached to this user's authentication object.
         *
         * @return the LDAP context
         */

       
public InitialLdapContext getContext() {
               
return context;
       
}

       
/**
         * Attach an LDAP context to this user's authentication object.
         *
         * @param context
         *          the LDAP context
         */

       
public void setContext(InitialLdapContext context) {
               
this.context = context;
       
}

}


'개발자세상' 카테고리의 다른 글

박대연, 그리고 안철수  (1) 2009.09.24
LDAP Query  (0) 2009.09.08
Spring Security 와 Active Directory 연동  (0) 2009.09.01
UML 2.0 과 유스케이스 강의를 듣고  (0) 2009.08.28
RPM 관련 명령  (0) 2009.08.18
Posted by headiron
,

B3 관련하여 본사 AD와 연동을 위해 Spring Security와 AD연동을 테스트 해 보았다.

아래와 같이 spring 설정을 하게 되면 Spring Security는 잘 동작 한다.

이 경우 인증 뿐만 아니라 AD의 그룹기능을 이용하여 Spring Security의 Role도 연동할 수 있게 된다. ( DefaultLdapAuthoritiesPopulator class의 "rolePrefix" 부분을 설정하면 AD의 그룹명 앞에 Prefix를 붙여서 작업할 수 있다. )

하지만 이 경우의 문제점은 AD의 관리자 권한을 가지고 있어야만 된다는 것이다.

여러 모로 생각해 보아도 우리에게 AD 관리자 권한을 열어 줄리는 만무한데...-.-

아래 내용은 http://maniezhilan.blogspot.com/2008/10/spring-security-204-with-active.html 에서 퍼왔음.

[applicationContext-security.xml]
 
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:s="http://www.springframework.org/schema/security"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
 http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
 <s:http>
         <s:intercept-url pattern="/secure/extreme/**" access="ROLE_eCommunications"/>
         <s:intercept-url pattern="/secure/**" access="IS_AUTHENTICATED_REMEMBERED"/>
         <s:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
         <s:form-login/>
         <s:anonymous/>
         <s:logout/>
 </s:http>
 <bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
  <constructor-arg value="ldap://192.168.192.71:3268/DC=dev,DC=247realmedia,DC=co,DC=kr?sAMAccountName?sub?(objectClass=*)"/>
  <property name="userDn" value="CN=kim ducheol,CN=Users,DC=dev,DC=247realmedia,DC=co,DC=kr"/>
  <property name="password" value="XXXXXXX"/>
 </bean>
 <bean id="secondLdapProvider" class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
  <s:custom-authentication-provider />
  <constructor-arg>
   <bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
    <constructor-arg ref="contextSource"/>
    <property name="userSearch">
     <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
      <constructor-arg index="0" value=""/>
      <constructor-arg index="1" value="(&amp;(sAMAccountName={0})(objectclass=user))"/>
      <constructor-arg index="2" ref="contextSource" />
     </bean>    
    </property>
   </bean>
  </constructor-arg>
  <constructor-arg>
   <bean class="org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator">
    <constructor-arg ref="contextSource" />
    <constructor-arg value="" />
    <property name="rolePrefix" value="ROLE_"/>
    <property name="searchSubtree" value="true"/>
    <property name="convertToUpperCase" value="false"/>
   </bean>
  </constructor-arg>
 </bean>
</beans>

'개발자세상' 카테고리의 다른 글

LDAP Query  (0) 2009.09.08
Spring Security 와 Active Directory 연동 -2  (0) 2009.09.01
UML 2.0 과 유스케이스 강의를 듣고  (0) 2009.08.28
RPM 관련 명령  (0) 2009.08.18
kernel ARG_MAX 값  (0) 2009.05.08
Posted by headiron
,