인터넷을 뒤지다 보니 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
,