View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.shiro.authc.credential;
20  
21  import org.apache.shiro.authc.AuthenticationInfo;
22  import org.apache.shiro.authc.AuthenticationToken;
23  import org.apache.shiro.crypto.hash.Hash;
24  
25  /**
26   * A {@link CredentialsMatcher} that employs best-practices comparisons for hashed text passwords.
27   * <p/>
28   * This implementation delegates to an internal {@link PasswordService} to perform the actual password
29   * comparison.  This class is essentially a bridge between the generic CredentialsMatcher interface and the
30   * more specific {@code PasswordService} component.
31   *
32   * @since 1.2
33   */
34  public class PasswordMatcher implements CredentialsMatcher {
35  
36      private PasswordService passwordService;
37  
38      public PasswordMatcher() {
39          this.passwordService = new DefaultPasswordService();
40      }
41  
42      public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
43  
44          PasswordService service = ensurePasswordService();
45  
46          Object submittedPassword = getSubmittedPassword(token);
47          Object storedCredentials = getStoredPassword(info);
48          assertStoredCredentialsType(storedCredentials);
49  
50          if (storedCredentials instanceof Hash) {
51              Hash hashedPassword = (Hash)storedCredentials;
52              HashingPasswordService hashingService = assertHashingPasswordService(service);
53              return hashingService.passwordsMatch(submittedPassword, hashedPassword);
54          }
55          //otherwise they are a String (asserted in the 'assertStoredCredentialsType' method call above):
56          String formatted = (String)storedCredentials;
57          return passwordService.passwordsMatch(submittedPassword, formatted);
58      }
59  
60      private HashingPasswordService assertHashingPasswordService(PasswordService service) {
61          if (service instanceof HashingPasswordService) {
62              return (HashingPasswordService) service;
63          }
64          String msg = "AuthenticationInfo's stored credentials are a Hash instance, but the " +
65                  "configured passwordService is not a " +
66                  HashingPasswordService.class.getName() + " instance.  This is required to perform Hash " +
67                  "object password comparisons.";
68          throw new IllegalStateException(msg);
69      }
70  
71      private PasswordService ensurePasswordService() {
72          PasswordService service = getPasswordService();
73          if (service == null) {
74              String msg = "Required PasswordService has not been configured.";
75              throw new IllegalStateException(msg);
76          }
77          return service;
78      }
79  
80      protected Object getSubmittedPassword(AuthenticationToken token) {
81          return token != null ? token.getCredentials() : null;
82      }
83  
84      private void assertStoredCredentialsType(Object credentials) {
85          if (credentials instanceof String || credentials instanceof Hash) {
86              return;
87          }
88  
89          String msg = "Stored account credentials are expected to be either a " +
90                  Hash.class.getName() + " instance or a formatted hash String.";
91          throw new IllegalArgumentException(msg);
92      }
93  
94      protected Object getStoredPassword(AuthenticationInfo storedAccountInfo) {
95          Object stored = storedAccountInfo != null ? storedAccountInfo.getCredentials() : null;
96          //fix for https://issues.apache.org/jira/browse/SHIRO-363
97          if (stored instanceof char[]) {
98              stored = new String((char[])stored);
99          }
100         return stored;
101     }
102 
103     public PasswordService getPasswordService() {
104         return passwordService;
105     }
106 
107     public void setPasswordService(PasswordService passwordService) {
108         this.passwordService = passwordService;
109     }
110 }