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.authc.AuthenticationInfo;
022    import org.apache.shiro.authc.AuthenticationToken;
023    import org.apache.shiro.codec.CodecSupport;
024    import org.slf4j.Logger;
025    import org.slf4j.LoggerFactory;
026    
027    import java.util.Arrays;
028    
029    
030    /**
031     * Simple CredentialsMatcher implementation.  Supports direct (plain) comparison for credentials of type
032     * byte[], char[], and Strings, and if the arguments do not match these types, then reverts back to simple
033     * <code>Object.equals</code> comparison.
034     * <p/>
035     * <p>Hashing comparisons (the most common technique used in secure applications) are not supported by this class, but
036     * instead by the {@link org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher}.
037     *
038     * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher
039     * @since 0.9
040     */
041    public class SimpleCredentialsMatcher extends CodecSupport implements CredentialsMatcher {
042    
043        private static final Logger log = LoggerFactory.getLogger(SimpleCredentialsMatcher.class);
044    
045        /**
046         * Returns the {@code token}'s credentials.
047         * <p/>
048         * <p>This default implementation merely returns
049         * {@link AuthenticationToken#getCredentials() authenticationToken.getCredentials()} and exists as a template hook
050         * if subclasses wish to obtain the credentials in a different way or convert them to a different format before
051         * returning.
052         *
053         * @param token the {@code AuthenticationToken} submitted during the authentication attempt.
054         * @return the {@code token}'s associated credentials.
055         */
056        protected Object getCredentials(AuthenticationToken token) {
057            return token.getCredentials();
058        }
059    
060        /**
061         * Returns the {@code account}'s credentials.
062         * <p/>
063         * <p>This default implementation merely returns
064         * {@link AuthenticationInfo#getCredentials() account.getCredentials()} and exists as a template hook if subclasses
065         * wish to obtain the credentials in a different way or convert them to a different format before
066         * returning.
067         *
068         * @param info the {@code AuthenticationInfo} stored in the data store to be compared against the submitted authentication
069         *             token's credentials.
070         * @return the {@code account}'s associated credentials.
071         */
072        protected Object getCredentials(AuthenticationInfo info) {
073            return info.getCredentials();
074        }
075    
076        /**
077         * Returns {@code true} if the {@code tokenCredentials} argument is logically equal to the
078         * {@code accountCredentials} argument.
079         * <p/>
080         * <p>If both arguments are either a byte array (byte[]), char array (char[]) or String, they will be both be
081         * converted to raw byte arrays via the {@link #toBytes toBytes} method first, and then resulting byte arrays
082         * are compared via {@link Arrays#equals(byte[], byte[]) Arrays.equals(byte[],byte[])}.</p>
083         * <p/>
084         * <p>If either argument cannot be converted to a byte array as described, a simple Object <code>equals</code>
085         * comparison is made.</p>
086         * <p/>
087         * <p>Subclasses should override this method for more explicit equality checks.
088         *
089         * @param tokenCredentials   the {@code AuthenticationToken}'s associated credentials.
090         * @param accountCredentials the {@code AuthenticationInfo}'s stored credentials.
091         * @return {@code true} if the {@code tokenCredentials} are equal to the {@code accountCredentials}.
092         */
093        protected boolean equals(Object tokenCredentials, Object accountCredentials) {
094            if (log.isDebugEnabled()) {
095                log.debug("Performing credentials equality check for tokenCredentials of type [" +
096                        tokenCredentials.getClass().getName() + " and accountCredentials of type [" +
097                        accountCredentials.getClass().getName() + "]");
098            }
099            if (isByteSource(tokenCredentials) && isByteSource(accountCredentials)) {
100                if (log.isDebugEnabled()) {
101                    log.debug("Both credentials arguments can be easily converted to byte arrays.  Performing " +
102                            "array equals comparison");
103                }
104                byte[] tokenBytes = toBytes(tokenCredentials);
105                byte[] accountBytes = toBytes(accountCredentials);
106                return Arrays.equals(tokenBytes, accountBytes);
107            } else {
108                return accountCredentials.equals(tokenCredentials);
109            }
110        }
111    
112        /**
113         * This implementation acquires the {@code token}'s credentials
114         * (via {@link #getCredentials(AuthenticationToken) getCredentials(token)})
115         * and then the {@code account}'s credentials
116         * (via {@link #getCredentials(org.apache.shiro.authc.AuthenticationInfo) getCredentials(account)}) and then passes both of
117         * them to the {@link #equals(Object,Object) equals(tokenCredentials, accountCredentials)} method for equality
118         * comparison.
119         *
120         * @param token the {@code AuthenticationToken} submitted during the authentication attempt.
121         * @param info  the {@code AuthenticationInfo} stored in the system matching the token principal.
122         * @return {@code true} if the provided token credentials are equal to the stored account credentials,
123         *         {@code false} otherwise
124         */
125        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
126            Object tokenCredentials = getCredentials(token);
127            Object accountCredentials = getCredentials(info);
128            return equals(tokenCredentials, accountCredentials);
129        }
130    
131    }