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 */
019package org.apache.shiro.authc.credential;
020
021import org.apache.shiro.authc.AuthenticationInfo;
022import org.apache.shiro.authc.AuthenticationToken;
023import org.apache.shiro.codec.CodecSupport;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027import 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 */
041public 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}