001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     * http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.shiro.authc.credential;
020    
021    import org.apache.shiro.util.ByteSource;
022    
023    /**
024     * A {@code PasswordService} supports common use cases when using passwords as a credentials mechanism.
025     * <p/>
026     * Most importantly, implementations of this interface are expected to employ best-practices to ensure that
027     * passwords remain as safe as possible in application environments.
028     * <h2>Usage</h2>
029     * A {@code PasswordService} is used at two different times during an application's lifecycle:
030     * <ul>
031     * <li>When creating a user account or resetting their password</li>
032     * <li>When a user logs in, when passwords must be compared</li>
033     * </ul>
034     * <h3>Account Creation or Password Reset</h3>
035     * Whenever you create a new user account or reset that account's password, we must translate the end-user submitted
036     * raw/plaintext password value to a string format that is much safer to store.  You do that by calling the
037     * {@link #encryptPassword(Object)} method to create the safer value.  For
038     * example:
039     * <pre>
040     * String submittedPlaintextPassword = ...
041     * String encryptedValue = passwordService.encryptPassword(submittedPlaintextPassword);
042     * ...
043     * userAccount.setPassword(encryptedValue);
044     * userAccount.save(); //create or update to your data store
045     * </pre>
046     * Be sure to save this encrypted password in your data store and never the original/raw submitted password.
047     * <h3>Login Password Comparison</h3>
048     * Shiro performs the comparison during login automatically.  Along with your {@code PasswordService}, you just
049     * have to configure a {@link PasswordMatcher} on a realm that has password-based accounts.   During a login attempt,
050     * shiro will use the {@code PasswordMatcher} and the {@code PasswordService} to automatically compare submitted
051     * passwords.
052     * <p/>
053     * For example, if using Shiro's INI, here is how you might configure the PasswordMatcher and PasswordService:
054     * <pre>
055     * [main]
056     * ...
057     * passwordService = org.apache.shiro.authc.credential.DefaultPasswordService
058     * # configure the passwordService to use the settings you desire
059     * ...
060     * passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher
061     * passwordMatcher.passwordService = $passwordService
062     * ...
063     * # Finally, set the matcher on a realm that requires password matching for account authentication:
064     * myRealm = ...
065     * myRealm.credentialsMatcher = $passwordMatcher
066     * </pre>
067     *
068     * @see DefaultPasswordService
069     * @see PasswordMatcher
070     * @since 1.2
071     */
072    public interface PasswordService {
073    
074        /**
075         * Converts the specified plaintext password (usually acquired from your application's 'new user' or 'password reset'
076         * workflow) into a formatted string safe for storage.  The returned string can be safely saved with the
077         * corresponding user account record (e.g. as a 'password' attribute).
078         * <p/>
079         * It is expected that the String returned from this method will be presented to the
080         * {@link #passwordsMatch(Object, String) passwordsMatch(plaintext,encrypted)} method when performing a
081         * password comparison check.
082         * <h3>Usage</h3>
083         * The input argument type can be any 'byte backed' {@code Object} - almost always either a
084         * String or character array representing passwords (character arrays are often a safer way to represent passwords
085         * as they can be cleared/nulled-out after use.  Any argument type supported by
086         * {@link ByteSource.Util#isCompatible(Object)} is valid.
087         * <p/>
088         * For example:
089         * <pre>
090         * String rawPassword = ...
091         * String encryptedValue = passwordService.encryptPassword(rawPassword);
092         * </pre>
093         * or, identically:
094         * <pre>
095         * char[] rawPasswordChars = ...
096         * String encryptedValue = passwordService.encryptPassword(rawPasswordChars);
097         * </pre>
098         * <p/>
099         * The resulting {@code encryptedValue} should be stored with the account to be retrieved later during a
100         * login attempt.  For example:
101         * <pre>
102         * String encryptedValue = passwordService.encryptPassword(rawPassword);
103         * ...
104         * userAccount.setPassword(encryptedValue);
105         * userAccount.save(); //create or update to your data store
106         * </pre>
107         *
108         * @param plaintextPassword the raw password as 'byte-backed' object (String, character array, {@link ByteSource},
109         *                          etc) usually acquired from your application's 'new user' or 'password reset' workflow.
110         * @return the encrypted password, formatted for storage.
111         * @throws IllegalArgumentException if the argument cannot be easily converted to bytes as defined by
112         *                                  {@link ByteSource.Util#isCompatible(Object)}.
113         * @see ByteSource.Util#isCompatible(Object)
114         */
115        String encryptPassword(Object plaintextPassword) throws IllegalArgumentException;
116    
117        /**
118         * Returns {@code true} if the {@code submittedPlaintext} password matches the existing {@code saved} password,
119         * {@code false} otherwise.
120         * <h3>Usage</h3>
121         * The {@code submittedPlaintext} argument type can be any 'byte backed' {@code Object} - almost always either a
122         * String or character array representing passwords (character arrays are often a safer way to represent passwords
123         * as they can be cleared/nulled-out after use.  Any argument type supported by
124         * {@link ByteSource.Util#isCompatible(Object)} is valid.
125         * <p/>
126         * For example:
127         * <pre>
128         * String submittedPassword = ...
129         * passwordService.passwordsMatch(submittedPassword, encryptedPassword);
130         * </pre>
131         * or similarly:
132         * <pre>
133         * char[] submittedPasswordCharacters = ...
134         * passwordService.passwordsMatch(submittedPasswordCharacters, encryptedPassword);
135         * </pre>
136         *
137         * @param submittedPlaintext a raw/plaintext password submitted by an end user/Subject.
138         * @param encrypted          the previously encrypted password known to be associated with an account.
139         *                           This value is expected to have been previously generated from the
140         *                           {@link #encryptPassword(Object) encryptPassword} method (typically
141         *                           when the account is created or the account's password is reset).
142         * @return {@code true} if the {@code submittedPlaintext} password matches the existing {@code saved} password,
143         *         {@code false} otherwise.
144         * @see ByteSource.Util#isCompatible(Object)
145         */
146        boolean passwordsMatch(Object submittedPlaintext, String encrypted);
147    }