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