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