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.lang.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 LOGGER = 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 (LOGGER.isDebugEnabled()) { 096 LOGGER.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 (LOGGER.isDebugEnabled()) { 102 LOGGER.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}