Comment s'authentifier contre un serveur Active Directory en utilisant la sécurité du printemps?

j'écris une application Web de printemps qui demande aux utilisateurs de se connecter. Mon entreprise a un serveur Active Directory que j'aimerais utiliser à cette fin. Cependant, j'ai du mal à utiliser la sécurité du ressort pour me connecter au serveur.

J'utilise Spring 2.5.5 et Spring Security 2.0.3, avec Java 1.6.

si je change L'URL LDAP à la mauvaise adresse IP, il ne jette pas une exception ou quoi que ce soit, donc je me demande si c'est même essayer pour se connecter au serveur pour commencer.

bien que l'application Web démarre très bien, toute information que j'entre dans la page de connexion est rejetée. J'avais déjà utilisé un InMemoryDaoImpl, qui fonctionnait bien, donc le reste de mon application semble être configuré correctement.

Voici mon liées à la sécurité des haricots:

  <beans:bean id="ldapAuthProvider" class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
    <beans:constructor-arg>
      <beans:bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
        <beans:constructor-arg ref="initialDirContextFactory" />
        <beans:property name="userDnPatterns">
          <beans:list>
            <beans:value>CN={0},OU=SBSUsers,OU=Users,OU=MyBusiness,DC=Acme,DC=com</beans:value>
          </beans:list>
        </beans:property>
      </beans:bean>
    </beans:constructor-arg>
  </beans:bean>

  <beans:bean id="userDetailsService" class="org.springframework.security.userdetails.ldap.LdapUserDetailsManager">
    <beans:constructor-arg ref="initialDirContextFactory" />
  </beans:bean>

  <beans:bean id="initialDirContextFactory" class="org.springframework.security.ldap.DefaultInitialDirContextFactory">
    <beans:constructor-arg value="ldap://192.168.123.456:389/DC=Acme,DC=com" />
  </beans:bean>
28
demandé sur GEOCHET 2008-09-17 19:44:00

8 réponses

j'ai eu la même expérience que vous et j'ai fini par écrire un fournisseur d'authentification personnalisé qui fait une requête LDAP contre le serveur Active Directory.

donc mes haricots liés à la sécurité sont:

<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>

puis la classe LdapAuthenticationProvider:

/**
 * 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;
    }

}

puis la classe LdapAuthenticatorImpl:

/**
 * 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 = "";
        }
    }

}

et enfin, la classe LdapAuthenticationToken:

/**
 * <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;
    }

}

Vous remarquerez que il y a quelques morceaux qui vous ne pourriez pas besoin.

Par exemple, mon application doit retenir le succès connecté LDAP contexte pour une utilisation ultérieure par l'utilisateur une fois connecté, l'application est de permettre aux utilisateurs de se connecter avec leurs identifiants AD et ensuite effectuer d'autres AD-fonctions associées. Donc, à cause de cela, j'ai un token d'authentification personnalisé, LdapAuthenticationToken, que je fais circuler (plutôt que le token D'authentification par défaut de Spring) qui me permet d'attacher le LDAP cadre. Dans LdapAuthenticationProvider.authenticate (), je crée ce token et je le renvoie; dans LdapAuthenticatorImpl.authenticate (), j'attache le contexte connecté à l'objet return afin qu'il puisse être ajouté à L'objet Spring authentication de l'utilisateur.

également dans LdapAuthenticationProvider.authenticate (), j'attribue à tous les utilisateurs connectés le rôle de ROLE_USER -- c'est ce qui me permet ensuite de tester ce rôle dans Mes éléments d'intercept-url. Vous voudrez faire ce match quel que soit le rôle vous voulez tester, ou même assigner des rôles basés sur des groupes Active Directory ou n'importe quoi d'autre.

enfin, et un corollaire à cela, la façon dont j'ai mis en œuvre LdapAuthenticationProvider.authenticate () donne à tous les utilisateurs ayant des comptes publicitaires valides le même rôle de ROLE_USER. Évidemment, dans cette méthode, vous pouvez effectuer des tests supplémentaires sur l'utilisateur (i.e., l'utilisateur est-il dans un groupe publicitaire spécifique?) et attribuer les rôles de cette façon, ou même tester pour une certaine condition avant même d'accorder à l'utilisateur l'accès .

36
répondu delfuego 2008-09-17 18:54:15

pour référence, Spring Security 3.1 a un fournisseur d'authentification spécifiquement pour Active Directory.

16
répondu Shaun the Sheep 2012-01-04 22:20:49

juste pour mettre cela à jour. Spring Security 3.0 a un package complet avec des implémentations par défaut dédiées à ldap-bind ainsi qu'à la requête et à la comparaison d'authentification.

2
répondu er4z0r 2010-03-16 13:59:40

j'ai été capable de m'authentifier contre active directory en utilisant la sécurité spring 2.0.4.

j'ai documenté les paramètres

http://maniezhilan.blogspot.com/2008/10/spring-security-204-with-active.html

1
répondu 2008-10-18 16:57:33

Spring Security 3.1 dispose d'un fournisseur d'authentification spécifique à Active Directory.

voici le détail de la façon dont cela peut être facilement fait en utilisant ActiveDirectoryLdapAuthenticationprovider.

En ressources.groovy:

ldapAuthProvider1(ActiveDirectoryLdapAuthenticationProvider,
        "mydomain.com",
        "ldap://mydomain.com/"
)

Dans Config.groovy:

grails.plugin.springsecurity.providerNames = ['ldapAuthProvider1']

C'est tout le code dont vous avez besoin. Vous pouvez enlever tous les autres grains.plugin.springsecurity.ldap.* paramètre dans la configuration.groovy comme ils ne s'appliquent pas à cette ANNONCE de l'installation.

Pour en savoir plus, consultez: http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ldap-active-directory

1
répondu Cookalino 2014-10-20 08:17:10

L'authentification LDAP sans SSL n'est pas sûre. Je suggère D'utiliser le protocole LDAPS:\ pour l'authentification. Il ne nécessite pas de changement majeur sur la partie de printemps, mais vous pouvez courir avec quelques problèmes liés aux certificats. Voir LDAP authentification Active Directory au Printemps avec SSL pour plus de détails

0
répondu Gaurav 2011-11-16 02:28:28

de la réponse de Luc ci-dessus:

pour référence, Spring Security 3.1 a un fournisseur d'authentification [spécifiquement pour Active Directory] [1].

[1]: http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ldap-active-directory

j'ai essayé la sécurité de Spring 3.1.1: il y a quelques légères modifications de ldap - les groupes Active directory l'utilisateur est un membre de venir à travers comme étui d'origine.

auparavant sous ldap les groupes étaient capitalisés et préfixés avec "ROLE__", ce qui les rendait faciles à trouver avec une recherche de texte dans un projet mais évidemment pourrait cas problèmes dans un groupe unix si pour une raison étrange avait 2 groupes séparés seulement différenciés par cas(c'est-à-dire les comptes et les comptes).

la syntaxe exige aussi la spécification manuelle du nom et du port du contrôleur de domaine, ce qui rend la redondance un peu effrayante. Il y a sûrement une façon de chercher L'enregistrement SRV DNS pour le domaine en java, c'est-à-dire l'équivalent de(à partir de Samba 4 howto):

$ host -t SRV _ldap._tcp.samdom.example.com.
_ldap._tcp.samdom.example.com has SRV record 0 100 389 samba.samdom.example.com.

suivi d'une recherche régulière:

$ host -t A samba.samdom.example.com.
samba.samdom.example.com has address 10.0.0.1

(en fait pourrait avoir besoin de rechercher _kerberos SRV record aussi...)

ce qui précède était avec Samba4.0rc1, nous sommes en train de passer progressivement de Samba 3.environnement X LDAP à Samba AD one.

0
répondu rjc730 2012-09-17 23:26:31

Si vous utilisez Printemps sécurité 4 vous pouvez aussi implémenter la même chose en utilisant classe donnée

  • SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {


static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfig.class);

@Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
              .antMatchers("/").permitAll()
              .anyRequest().authenticated();
            .and()
              .formLogin()
            .and()
              .logout();
}

@Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
    ActiveDirectoryLdapAuthenticationProvider authenticationProvider = 
        new ActiveDirectoryLdapAuthenticationProvider("<domain>", "<url>");

    authenticationProvider.setConvertSubErrorCodesToExceptions(true);
    authenticationProvider.setUseAuthenticationRequestCredentials(true);

    return authenticationProvider;
}
}
0
répondu Riddhi Gohil 2016-04-20 09:11:22