Frage

Ich schreibe eine Frühlings-Web-Anwendung, bei der Benutzer sich anmelden. Meine Firma hat einen Active Directory-Server, Ich mag würde die Verwendung von für diesen Zweck zu machen. Aber ich habe Probleme beim Spring Security mit dem Server zu verbinden.

Ich bin mit Spring 2.5.5 und Spring Security 2.0.3, zusammen mit Java 1.6.

Wenn ich die LDAP-URL an die falsche IP-Adresse ändern, ist es nicht eine Ausnahme oder irgendetwas, werfen so frage ich mich, ob es überhaupt ist versuchen auf dem Server zu verbinden, mit zu beginnen.

Auch wenn die Web-Anwendung nur gut anläuft, jede Information, die ich in die Login-Seite eingeben, wird abgelehnt. Ich hatte zuvor verwendet eine InMemoryDaoImpl, die gut funktioniert, so dass der Rest meiner Anwendung scheint korrekt konfiguriert werden.

Hier sind meine sicherheitsrelevanten Bohnen:

  <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>
War es hilfreich?

Lösung

Ich hatte die gleiche Knallen-my-Kopf-gegen-die-Wand-Erfahrung, die Sie haben, und am Ende eines benutzerdefinierten Authentifizierungsanbieter zu schreiben, die eine LDAP-Abfrage für den Active Directory-Server des Fall ist.

Also meine sicherheitsrelevanten Bohnen sind:

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

Dann wird die LdapAuthenticationProvider Klasse:

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

}

Dann wird die LdapAuthenticatorImpl Klasse:

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

}

Und schließlich die LdapAuthenticationToken Klasse:

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

}

Sie werden feststellen, dass es ein paar Bits gibt es, dass Sie vielleicht nicht benötigen.

Zum Beispiel meiner Anwendung benötigte die erfolgreich angemeldeten in LDAP-Kontext zur weiteren Verwendung durch den Benutzer einmal angemeldet zu behalten - die App Zweck ist es, Benutzer über ihre AD-Anmeldeinformationen melden Sie sich an zu lassen und führen Sie dann weitere AD-verwandte Funktionen. Also aus diesem Grund habe ich eine benutzerdefinierte Authentifizierungstoken, LdapAuthenticationToken, dass ich um passieren (und nicht Spring Standardauthentifizierungstoken), die mir den LDAP-Kontext befestigen können. In LdapAuthenticationProvider.authenticate (), erzeuge ich diese Token und geben es wieder aus; in LdapAuthenticatorImpl.authenticate (), lege ich den angemeldeten in Zusammenhang mit dem Rück Objekt, so dass es auf die Feder des Benutzers Authentifikationsobjekt hinzugefügt werden kann.

Auch in LdapAuthenticationProvider.authenticate (), ordne ich alle angemeldeten Benutzer die ROLE_USER Rolle - das ist, was mich dann Test für diese Rolle in meinem Intercept-url Elemente können. Sie werden dieses Spiel machen wollen, was Rolle Sie, testen möchten oder sogar Rollen zuweisen basierend auf Active Directory-Gruppen oder was auch immer.

Schließlich, und eine logische Folge, dass die Art, wie ich umgesetzt LdapAuthenticationProvider.authenticate () gibt alle Benutzer mit gültigen AD die gleiche ROLE_USER Rolle ausmacht. Offensichtlich in diesem Verfahren Sie weitere Tests auf dem Benutzer durchführen können (dh der Benutzer in einer bestimmten AD-Gruppe?) Und die Zuweisung von Rollen, die Art und Weise, oder auch Test für eine Bedingung, bevor auch den Benutzer des Zugriff gewährt in alle .

Andere Tipps

Als Referenz Spring Security 3.1 hat einen Authentifizierungsanbieter speziell für Active Directory .

Just zu bringen, dies zu einem up-to-date-Status. Spring Security 3.0 hat eine Komplettpaket mit Standardimplementierungen sowie Abfrage von lDAP-bind gewidmet und Authentifizierung vergleichen.

Ich konnte gegen Active Directory-Sicherheit 2.0.4 mit Feder authentifizieren.

ich die Einstellungen dokumentiert

http: // maniezhilan .blogspot.com / 2008/10 / Feder-security-204-mit-active.html

Wie in Lukes Antwort oben:

  

Spring Security 3.1 hat einen Authentifizierungsanbieter speziell für Active Directory.

Hier ist das Detail, wie kann dies leicht mit ActiveDirectoryLdapAuthenticationProvider erfolgen.

In resources.groovy:

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

In Config.groovy:

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

Dies ist der gesamte Code Sie benötigen. Sie können so ziemlich alle anderen grails.plugin.springsecurity.ldap entfernen. * Einstellungen in Config.groovy, wie sie zu dieser AD-Setup nicht gelten.

Für die Dokumentation finden Sie unter: http: / /docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ldap-active-directory

LDAP-Authentifizierung ohne SSL ist nicht sicher, jeder kann die Anmeldeinformationen des Benutzers sehen, wenn diejenigen auf LDAP-Server transffered werden. Ich schlage vor, mit LDAPS: \ Protokoll für die Authentifizierung. Es erfordert keine große Veränderung auf Federteil Sie können jedoch mit einigen Problemen laufen im Zusammenhang mit Zertifikaten. Siehe LDAP Active Directory-Authentifizierung im Frühjahr mit SSL mehr Details

Von Lukes Antwort oben:

  

Als Referenz Spring Security 3.1 hat einen Authentifizierungsanbieter   [Speziell für Active Directory] [1].

     

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

habe ich versucht, die oben mit Spring Security 3.1.1: Es gibt einige geringfügige Änderungen von ldap - die Active Directory-Gruppen der Benutzer ist Mitglied kommen durch als ursprünglich Fall

.

Bisher unter ldap waren die Gruppen aktiviert und mit dem Präfix „ROLE_“, die sie leicht gemacht, mit einem Textsuche in einem Projekt zu finden, aber offensichtlich zu Problemen in einer Unix-Gruppe Fall, wenn aus irgendeinem seltsamen Grund 2 separaten Gruppen hatte nur differenziert von Fall (dh Konten und Konten).

Auch die Syntax erfordert manuelle Vorgabe des Domänencontrollers Namen und Port, die es ein bisschen beängstigend für Redundanz macht. Sicherlich gibt es eine Möglichkeit, den SRV DNS-Eintrag für die Domain in Java, das heißt Äquivalent (von Samba 4 Howto) des Nachschlagens:

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

, gefolgt von regelmäßigem A-Lookup:

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

(Eigentlich muss möglicherweise _kerberos SRV-Eintrag Nachschlag ...)

Das oben war mit Samba4.0rc1, wir aktualisieren progressiv von Samba 3.x LDAP-Umgebung zu Samba AD ein.

  

Wenn Sie Frühling verwenden Sicherheit 4 können Sie auch implementieren diese verwendet   gegebene Klasse

     
      
  • 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;
}
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top