View Javadoc

1   /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.springframework.security.providers.ldap.authenticator;
17  
18  import org.springframework.security.Authentication;
19  import org.springframework.security.BadCredentialsException;
20  import org.springframework.security.ldap.LdapUtils;
21  import org.springframework.security.ldap.SpringSecurityLdapTemplate;
22  import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
23  import org.springframework.security.providers.encoding.PasswordEncoder;
24  import org.springframework.security.userdetails.UsernameNotFoundException;
25  import org.springframework.ldap.NameNotFoundException;
26  import org.springframework.ldap.core.DirContextOperations;
27  import org.springframework.ldap.core.support.BaseLdapPathContextSource;
28  import org.springframework.util.Assert;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  
33  import java.util.Iterator;
34  
35  
36  /**
37   * An {@link org.springframework.security.providers.ldap.LdapAuthenticator LdapAuthenticator} which compares the login
38   * password with the value stored in the directory using a remote LDAP "compare" operation.
39   *
40   * <p>
41   * If passwords are stored in digest form in the repository, then a suitable {@link PasswordEncoder}
42   * implementation must be supplied. By default, passwords are encoded using the {@link LdapShaPasswordEncoder}.
43   *
44   * @author Luke Taylor
45   * @version $Id: PasswordComparisonAuthenticator.java 2729 2008-03-13 16:49:19Z luke_t $
46   */
47  public final class PasswordComparisonAuthenticator extends AbstractLdapAuthenticator {
48      //~ Static fields/initializers =====================================================================================
49  
50      private static final Log logger = LogFactory.getLog(PasswordComparisonAuthenticator.class);
51  
52      //~ Instance fields ================================================================================================
53  
54      private PasswordEncoder passwordEncoder = new LdapShaPasswordEncoder();
55      private String passwordAttributeName = "userPassword";
56  
57      //~ Constructors ===================================================================================================
58  
59      public PasswordComparisonAuthenticator(BaseLdapPathContextSource contextSource) {
60          super(contextSource);
61      }
62  
63      //~ Methods ========================================================================================================
64  
65      public DirContextOperations authenticate(final Authentication authentication) {
66          Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
67                  "Can only process UsernamePasswordAuthenticationToken objects");
68          // locate the user and check the password
69  
70          DirContextOperations user = null;
71          String username = authentication.getName();
72          String password = (String)authentication.getCredentials();
73  
74          Iterator dns = getUserDns(username).iterator();
75  
76          SpringSecurityLdapTemplate ldapTemplate = new SpringSecurityLdapTemplate(getContextSource());
77  
78          while (dns.hasNext() && user == null) {
79              final String userDn = (String) dns.next();
80  
81              try {
82                  user = ldapTemplate.retrieveEntry(userDn, getUserAttributes());
83              } catch (NameNotFoundException ignore) {
84              }
85          }
86  
87          if (user == null && getUserSearch() != null) {
88              user = getUserSearch().searchForUser(username);
89          }
90  
91          if (user == null) {
92              throw new UsernameNotFoundException("User not found: " + username, username);
93          }
94  
95          if (logger.isDebugEnabled()) {
96              logger.debug("Performing LDAP compare of password attribute '" + passwordAttributeName + "' for user '" +
97                      user.getDn() +"'");
98          }
99  
100         String encodedPassword = passwordEncoder.encodePassword(password, null);
101         byte[] passwordBytes = LdapUtils.getUtf8Bytes(encodedPassword);
102 
103         if (!ldapTemplate.compare(user.getDn().toString(), passwordAttributeName, passwordBytes)) {
104             throw new BadCredentialsException(messages.getMessage("PasswordComparisonAuthenticator.badCredentials",
105                     "Bad credentials"));
106         }
107 
108         return user;
109     }
110 
111     public void setPasswordAttributeName(String passwordAttribute) {
112         Assert.hasLength(passwordAttribute, "passwordAttributeName must not be empty or null");
113         this.passwordAttributeName = passwordAttribute;
114     }
115 
116     public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
117         Assert.notNull(passwordEncoder, "passwordEncoder must not be null.");
118         this.passwordEncoder = passwordEncoder;
119     }
120 }