2008-09-17 7 views
27

Ich schreibe eine Spring Web-Anwendung, die Benutzer zur Anmeldung benötigt. Meine Firma hat einen Active Directory Server, den ich für diesen Zweck nutzen möchte. Ich habe jedoch Probleme mit Spring Security, um eine Verbindung zum Server herzustellen.Wie authentifizieren Sie sich mithilfe von Spring Security bei einem Active Directory-Server?

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

Wenn ich die LDAP-URL auf die falsche IP-Adresse ändere, wirft es keine Ausnahme oder irgendetwas, also frage ich mich, ob es versucht, Verbindung mit dem Server zu beginnen.

Obwohl die Webanwendung einwandfrei startet, werden alle Informationen, die ich auf der Anmeldeseite eintrage, zurückgewiesen. Ich hatte zuvor ein InMemoryDaoImpl verwendet, was gut funktionierte, so scheint der Rest meiner Anwendung korrekt konfiguriert zu sein.

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

Dies ist nicht wirklich eine Antwort, sondern eine klärende Frage - haben Sie das Logging für die Sicherheitspakete für den Frühling auf Hochtouren gebracht? –

+0

Ich habe das Logging für alles aktiviert. Keine Nachrichten werden protokolliert ... Ich habe meine Frage mit meiner Log4J-Konfiguration aktualisiert. – Michael

+0

Sagen Sie mir die dafür benötigten JAR-Dateien. Bitte – addy

Antwort

35

hatte ich die gleiche hämmern-my-Kopf-gegen-die-Wand-Erfahrung, die Sie haben, und am Ende einen benutzerdefinierten Authentifizierungsanbieter zu schreiben, die eine LDAP tut Abfrage für den Active Directory-Server.

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 bemerken, dass ein paar Bits drin sind, die Sie nicht brauchen könnten.

Zum Beispiel musste meine App den erfolgreich eingeloggten LDAP-Kontext für die weitere Nutzung durch den Benutzer beibehalten, nachdem er sich angemeldet hatte. Der Zweck der App besteht darin, Benutzer über ihre AD-Anmeldeinformationen einzuloggen und weitere AD- verwandte Funktionen. Aus diesem Grund habe ich ein benutzerdefiniertes Authentifizierungstoken, LdapAuthenticationToken, das ich weitergeben kann (anstelle des standardmäßigen Authentifizierungstokens von Spring), mit dem ich den LDAP-Kontext anhängen kann. In LdapAuthenticationProvider.authenticate() erstelle ich dieses Token und gebe es zurück. In LdapAuthenticatorImpl.authenticate() füge ich den eingeloggten Kontext an das Rückgabeobjekt an, sodass es dem Spring-Authentifizierungsobjekt des Benutzers hinzugefügt werden kann.

In LdapAuthenticationProvider.authenticate(), ich alle angemeldeten Benutzer die ROLE_USER-Rolle zuweisen - das ist, was ich dann für diese Rolle in meine Intercept-URL-Elemente testen kann. Sie möchten, dass diese Übereinstimmung mit der Rolle übereinstimmt, die Sie testen möchten, oder weisen Sie Rollen basierend auf Active Directory-Gruppen oder was auch immer zu.

Schließlich, und die logische Folge, gibt die Art, wie ich LdapAuthenticationProvider.authenticate() implementiert, allen Benutzern mit gültigen AD-Konten die gleiche ROLE_USER-Rolle. Offensichtlich können Sie bei diesem Verfahren weitere Tests mit dem Benutzer durchführen (d. H. Ist der Benutzer in einer bestimmten AD-Gruppe?) Und Rollen auf diese Weise zuweisen oder sogar auf bestimmte Bedingungen testen, bevor Sie dem Benutzer unter alle den Zugriff gewähren.

+0

Erzähl mir die JAR-Dateien dafür erforderlich. Bitte. – addy

+0

Ich hatte das gleiche Problem. Aber wie viele dieser Methoden im Frühling neue Versionen verwarfen, kann ich den gleichen Job nicht mehr so ​​machen. Ich habe meine Frage hier http://stackoverflow.com/questions/32070142/spring-security-configuratition-to-authenticate-ldap-user gestellt. Ich werde mich wundern, wenn Sie mir dabei helfen können @ delfuego – moha

2

Gerade diesen Status eines up-to-date zu bringen. Spring Security 3.0 verfügt über eine complete package mit Standardimplementierungen, die der LDAP-Bindung sowie der Abfrage- und Vergleichsauthentifizierung gewidmet sind.

0

LDAP-Authentifizierung ohne SSL ist nicht sicher. Jeder Benutzer kann die Anmeldeinformationen des Benutzers sehen, wenn diese auf den LDAP-Server übertragen werden. Ich empfehle die Verwendung von LDAPS: \ Protokoll für die Authentifizierung. Es erfordert keine größere Änderung am Federteil, aber Sie könnten mit einigen Problemen im Zusammenhang mit Zertifikaten laufen. Weitere Details finden Sie unter LDAP Active Directory authentication in Spring with SSL

16

Als Referenz hat Spring Security 3.1 einen Authentifizierungsanbieter specifically for Active Directory.

+1

+1 Dies sollte die beste Antwort sein. Aus Erfahrung kann ich sagen, dass Sie das Leben sehr hart für sich selbst machen, wenn Sie LdapAuthenticationProvider für die Authentifizierung gegen AD verwenden. In modernen Versionen von Spring können Sie in weniger als 5 Zeilen Code machen, was Sie wollen. Siehe den Link, den Luke oben angegeben hat. Ich habe auch Details in meiner Antwort unten angegeben. – Cookalino

0

Von Luke Antwort oben:

Als Referenz Spring Security 3.1 einen Authentifizierungsanbieter [speziell für Active Directory] hat [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 Original Brillenetui .

Früher unter LDAP wurden die Gruppen groß geschrieben und mit "ROLE_" versehen, was sie mit einer Textsuche in einem Projekt leicht auffindbar machte, aber offensichtlich Probleme in einer Unix-Gruppe hatte, wenn sie aus irgendeinem seltsamen Grund nur zwei separate Gruppen hatte differenziert nach Fall (zB Konten und Konten).

Auch die Syntax erfordert eine manuelle Angabe des Domänencontrollernamens und -ports, was die Redundanz etwas beängstigend macht. Sicherlich gibt es eine Möglichkeit, den SRV DNS-Eintrag für die Domain in Java, zB Ä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äßigen A-Lookup:

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

(Eigentlich könnte auch suchen _kerberos SRV-Eintrag zu ...)

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

0

Wie in Lukes Antwort oben:

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

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

In Ressourcen.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 alle anderen Einstellungen von grails.plugin.springsecurity.ldap. * In Config.groovy entfernen, da sie nicht auf diese AD-Konfiguration angewendet werden.

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

0

Wenn Sie Frühling verwenden Sicherheit 4 können Sie auch gleiche implementieren gegebenen Klasse

  • SecurityConfig.java mit
@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; 
} 
}