2013年1月17日 星期四

Configure LdapAuthenticationProvider Programmatically

LdapAuthenticationProvider基本的設定,將此bean塞到AuthenticationManager中即可。
SpringConfiguration
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.BindAuthenticator;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import com.gss.gmo.cao.spring.security.ldap.userdetails.UserDetailsServiceContextMapper;

@Configuration
public class SpringConfiguration {

    @SneakyThrows
    @Bean
    @Autowired
    LdapAuthenticationProvider ldapAuthenticationProvider(UserDetailsService userDetailsService, @Value("${ldap.server.url}") String serverUrl,
            @Value("${ldap.server.username}") String serverUsername, @Value("${ldap.server.password}") String serverPassword) {

        DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(serverUrl);
        contextSource.setUserDn(serverUsername);
        contextSource.setPassword(serverPassword);
        contextSource.afterPropertiesSet();

        FilterBasedLdapUserSearch filterBasedLdapUserSearch = new FilterBasedLdapUserSearch("OU=GSS,DC=gss,DC=com,DC=tw", "(CN={0})", contextSource);

        BindAuthenticator bindAuthenticator = new BindAuthenticator(contextSource);
        bindAuthenticator.setUserSearch(filterBasedLdapUserSearch);
        bindAuthenticator.afterPropertiesSet();

        LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(bindAuthenticator);
        ldapAuthenticationProvider.setUserDetailsContextMapper(new UserDetailsServiceContextMapper(userDetailsService));
        return ldapAuthenticationProvider;
    }

}
UserDetailsContextMapper的用途是在通過身分驗證之後,可以利用UserDetailsService取得使用者權限。
UserDetailsServiceContextMapper
import java.util.Collection;
import lombok.extern.apachecommons.CommonsLog;
import org.apache.commons.lang.Validate;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;

/**
 * @author linus_chien
 *
 */
@CommonsLog
public class UserDetailsServiceContextMapper implements UserDetailsContextMapper {

    private UserDetailsService userDetailsService;

    public UserDetailsServiceContextMapper(UserDetailsService userDetailsService) {
        Validate.notNull(userDetailsService);
        this.userDetailsService = userDetailsService;
    }

    @Override
    public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
        try {
            return userDetailsService.loadUserByUsername(username);
        } catch (UsernameNotFoundException e) {
            throw e;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new UsernameNotFoundException(e.getMessage());
        }
    }

    @Override
    public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
        throw new UnsupportedOperationException(
                "UserDetailsServiceContextMapper only supports reading from a context. Please use a subclass if mapUserToContext() is required.");
    }

}