Coverage Report - org.apache.shiro.realm.AuthenticatingRealm
 
Classes in this File Line Coverage Branch Coverage Complexity
AuthenticatingRealm
100%
111/111
75%
42/56
1.935
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.shiro.realm;
 20  
 
 21  
 import org.apache.shiro.authc.*;
 22  
 import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
 23  
 import org.apache.shiro.authc.credential.CredentialsMatcher;
 24  
 import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
 25  
 import org.apache.shiro.cache.Cache;
 26  
 import org.apache.shiro.cache.CacheManager;
 27  
 import org.apache.shiro.subject.PrincipalCollection;
 28  
 import org.apache.shiro.util.CollectionUtils;
 29  
 import org.apache.shiro.util.Initializable;
 30  
 import org.slf4j.Logger;
 31  
 import org.slf4j.LoggerFactory;
 32  
 
 33  
 import java.util.concurrent.atomic.AtomicInteger;
 34  
 
 35  
 
 36  
 /**
 37  
  * A top-level abstract implementation of the <tt>Realm</tt> interface that only implements authentication support
 38  
  * (log-in) operations and leaves authorization (access control) behavior to subclasses.
 39  
  * <h2>Authentication Caching</h2>
 40  
  * For applications that perform frequent repeated authentication of the same accounts (e.g. as is often done in
 41  
  * REST or Soap applications that authenticate on every request), it might be prudent to enable authentication
 42  
  * caching to alleviate constant load on any back-end data sources.
 43  
  * <p/>
 44  
  * This feature is disabled by default to retain backwards-compatibility with Shiro 1.1 and earlier.  It may be
 45  
  * enabled by setting {@link #setAuthenticationCachingEnabled(boolean) authenticationCachingEnabled} = {@code true}
 46  
  * (and configuring Shiro with a {@link CacheManager} of course), but <b>NOTE:</b>
 47  
  * <p/>
 48  
  * <b>ONLY enable authentication caching if either of the following is true for your realm implementation:</b>
 49  
  * <ul>
 50  
  * <li>The {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) doGetAuthenticationInfo}
 51  
  * implementation returns {@code AuthenticationInfo} instances where the
 52  
  * {@link org.apache.shiro.authc.AuthenticationInfo#getCredentials() credentials} are securely obfuscated and NOT
 53  
  * plaintext (raw) credentials. For example,
 54  
  * if your realm references accounts with passwords, that the {@code AuthenticationInfo}'s
 55  
  * {@link org.apache.shiro.authc.AuthenticationInfo#getCredentials() credentials} are safely hashed and salted or otherwise
 56  
  * fully encrypted.<br/><br/></li>
 57  
  * <li>The {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) doGetAuthenticationInfo}
 58  
  * implementation returns {@code AuthenticationInfo} instances where the
 59  
  * {@link org.apache.shiro.authc.AuthenticationInfo#getCredentials() credentials} are plaintext (raw) <b>AND</b> the
 60  
  * cache region storing the {@code AuthenticationInfo} instances WILL NOT overflow to disk and WILL NOT transmit cache
 61  
  * entries over an unprotected (non TLS/SSL) network (as might be the case with a networked/distributed enterprise cache).
 62  
  * This should be the case even in private/trusted/corporate networks.</li>
 63  
  * </ul>
 64  
  * <p/>
 65  
  * These points are very important because if authentication caching is enabled, this abstract class implementation
 66  
  * will place AuthenticationInfo instances returned from the subclass implementations directly into the cache, for
 67  
  * example:
 68  
  * <pre>
 69  
  * cache.put(cacheKey, subclassAuthenticationInfoInstance);
 70  
  * </pre>
 71  
  * <p/>
 72  
  * Enabling authentication caching is ONLY safe to do if the above two scenarios apply.  It is NOT safe to enable under
 73  
  * any other scenario.
 74  
  * <p/>
 75  
  * When possible, always represent and store credentials in a safe form (hash+salt or encrypted) to eliminate plaintext
 76  
  * visibility.
 77  
  * <h3>Authentication Cache Invalidation on Logout</h3>
 78  
  * If authentication caching is enabled, this implementation will attempt to evict (remove) cached authentication data
 79  
  * for an account during logout.  This can only occur if the
 80  
  * {@link #getAuthenticationCacheKey(org.apache.shiro.authc.AuthenticationToken)} and
 81  
  * {@link #getAuthenticationCacheKey(org.apache.shiro.subject.PrincipalCollection)} methods return the exact same value.
 82  
  * <p/>
 83  
  * The default implementations of these methods expect that the
 84  
  * {@link org.apache.shiro.authc.AuthenticationToken#getPrincipal()} (what the user submits during login) and
 85  
  * {@link #getAvailablePrincipal(org.apache.shiro.subject.PrincipalCollection) getAvailablePrincipal} (what is returned
 86  
  * by the realm after account lookup) return
 87  
  * the same exact value.  For example, the user submitted username is also the primary account identifier.
 88  
  * <p/>
 89  
  * However, if your application uses, say, a username for end-user login, but returns a primary key ID as the
 90  
  * primary principal after authentication, then you will need to override either
 91  
  * {@link #getAuthenticationCacheKey(org.apache.shiro.authc.AuthenticationToken) getAuthenticationCacheKey(token)} or
 92  
  * {@link #getAuthenticationCacheKey(org.apache.shiro.subject.PrincipalCollection) getAuthenticationCacheKey(principals)}
 93  
  * (or both) to ensure that the same cache key can be used for either object.
 94  
  * <p/>
 95  
  * This guarantees that the same cache key used to cache the data during authentication (derived from the
 96  
  * {@code AuthenticationToken}) will be used to remove the cached data during logout (derived from the
 97  
  * {@code PrincipalCollection}).
 98  
  * <h4>Unmatching Cache Key Values</h4>
 99  
  * If the return values from {@link #getAuthenticationCacheKey(org.apache.shiro.authc.AuthenticationToken)} and
 100  
  * {@link #getAuthenticationCacheKey(org.apache.shiro.subject.PrincipalCollection)} are not identical, cached
 101  
  * authentication data removal is at the mercy of your cache provider settings.  For example, often cache
 102  
  * implementations will evict cache entries based on a timeToIdle or timeToLive (TTL) value.
 103  
  * <p/>
 104  
  * If this lazy eviction capability of the cache product is not sufficient and you want discrete behavior
 105  
  * (highly recommended for authentication data), ensure that the return values from those two methods are identical in
 106  
  * the subclass implementation.
 107  
  *
 108  
  * @since 0.2
 109  
  */
 110  
 public abstract class AuthenticatingRealm extends CachingRealm implements Initializable {
 111  
 
 112  
     //TODO - complete JavaDoc
 113  
 
 114  1
     private static final Logger log = LoggerFactory.getLogger(AuthenticatingRealm.class);
 115  
 
 116  1
     private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();
 117  
 
 118  
     /**
 119  
      * The default suffix appended to the realm name used for caching authentication data.
 120  
      *
 121  
      * @since 1.2
 122  
      */
 123  
     private static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = ".authenticationCache";
 124  
 
 125  
     /**
 126  
      * Credentials matcher used to determine if the provided credentials match the credentials stored in the data store.
 127  
      */
 128  
     private CredentialsMatcher credentialsMatcher;
 129  
 
 130  
 
 131  
     private Cache<Object, AuthenticationInfo> authenticationCache;
 132  
 
 133  
     private boolean authenticationCachingEnabled;
 134  
     private String authenticationCacheName;
 135  
 
 136  
     /**
 137  
      * The class that this realm supports for authentication tokens.  This is used by the
 138  
      * default implementation of the {@link Realm#supports(org.apache.shiro.authc.AuthenticationToken)} method to
 139  
      * determine whether or not the given authentication token is supported by this realm.
 140  
      */
 141  
     private Class<? extends AuthenticationToken> authenticationTokenClass;
 142  
 
 143  
     /*-------------------------------------------
 144  
     |         C O N S T R U C T O R S           |
 145  
     ============================================*/
 146  
     public AuthenticatingRealm() {
 147  80
         this(null, new SimpleCredentialsMatcher());
 148  80
     }
 149  
 
 150  
     public AuthenticatingRealm(CacheManager cacheManager) {
 151  1
         this(cacheManager, new SimpleCredentialsMatcher());
 152  1
     }
 153  
 
 154  
     public AuthenticatingRealm(CredentialsMatcher matcher) {
 155  1
         this(null, matcher);
 156  1
     }
 157  
 
 158  82
     public AuthenticatingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
 159  82
         authenticationTokenClass = UsernamePasswordToken.class;
 160  
 
 161  
         //retain backwards compatibility for Shiro 1.1 and earlier.  Setting to true by default will probably cause
 162  
         //unexpected results for existing applications:
 163  82
         this.authenticationCachingEnabled = false;
 164  
 
 165  82
         int instanceNumber = INSTANCE_COUNT.getAndIncrement();
 166  82
         this.authenticationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
 167  82
         if (instanceNumber > 0) {
 168  81
             this.authenticationCacheName = this.authenticationCacheName + "." + instanceNumber;
 169  
         }
 170  
 
 171  82
         if (cacheManager != null) {
 172  1
             setCacheManager(cacheManager);
 173  
         }
 174  82
         if (matcher != null) {
 175  82
             setCredentialsMatcher(matcher);
 176  
         }
 177  82
     }
 178  
 
 179  
     /*--------------------------------------------
 180  
     |  A C C E S S O R S / M O D I F I E R S    |
 181  
     ============================================*/
 182  
 
 183  
     /**
 184  
      * Returns the <code>CredentialsMatcher</code> used during an authentication attempt to verify submitted
 185  
      * credentials with those stored in the system.
 186  
      * <p/>
 187  
      * <p>Unless overridden by the {@link #setCredentialsMatcher setCredentialsMatcher} method, the default
 188  
      * value is a {@link org.apache.shiro.authc.credential.SimpleCredentialsMatcher SimpleCredentialsMatcher} instance.
 189  
      *
 190  
      * @return the <code>CredentialsMatcher</code> used during an authentication attempt to verify submitted
 191  
      *         credentials with those stored in the system.
 192  
      */
 193  
     public CredentialsMatcher getCredentialsMatcher() {
 194  32
         return credentialsMatcher;
 195  
     }
 196  
 
 197  
     /**
 198  
      * Sets the CrendialsMatcher used during an authentication attempt to verify submitted credentials with those
 199  
      * stored in the system.  The implementation of this matcher can be switched via configuration to
 200  
      * support any number of schemes, including plain text comparisons, hashing comparisons, and others.
 201  
      * <p/>
 202  
      * <p>Unless overridden by this method, the default value is a
 203  
      * {@link org.apache.shiro.authc.credential.SimpleCredentialsMatcher} instance.
 204  
      *
 205  
      * @param credentialsMatcher the matcher to use.
 206  
      */
 207  
     public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
 208  116
         this.credentialsMatcher = credentialsMatcher;
 209  116
     }
 210  
 
 211  
     /**
 212  
      * Returns the authenticationToken class supported by this realm.
 213  
      * <p/>
 214  
      * <p>The default value is <tt>{@link org.apache.shiro.authc.UsernamePasswordToken UsernamePasswordToken.class}</tt>, since
 215  
      * about 90% of realms use username/password authentication, regardless of their protocol (e.g. over jdbc, ldap,
 216  
      * kerberos, http, etc).
 217  
      * <p/>
 218  
      * <p>If subclasses haven't already overridden the {@link Realm#supports Realm.supports(AuthenticationToken)} method,
 219  
      * they must {@link #setAuthenticationTokenClass(Class) set a new class} if they won't support
 220  
      * <tt>UsernamePasswordToken</tt> authentication token submissions.
 221  
      *
 222  
      * @return the authenticationToken class supported by this realm.
 223  
      * @see #setAuthenticationTokenClass
 224  
      */
 225  
     public Class getAuthenticationTokenClass() {
 226  24
         return authenticationTokenClass;
 227  
     }
 228  
 
 229  
     /**
 230  
      * Sets the authenticationToken class supported by this realm.
 231  
      * <p/>
 232  
      * <p>Unless overridden by this method, the default value is
 233  
      * {@link org.apache.shiro.authc.UsernamePasswordToken UsernamePasswordToken.class} to support the majority of applications.
 234  
      *
 235  
      * @param authenticationTokenClass the class of authentication token instances supported by this realm.
 236  
      * @see #getAuthenticationTokenClass getAuthenticationTokenClass() for more explanation.
 237  
      */
 238  
     public void setAuthenticationTokenClass(Class<? extends AuthenticationToken> authenticationTokenClass) {
 239  14
         this.authenticationTokenClass = authenticationTokenClass;
 240  14
     }
 241  
 
 242  
     /**
 243  
      * Sets an explicit {@link Cache} instance to use for authentication caching.  If not set and authentication
 244  
      * caching is {@link #isAuthenticationCachingEnabled() enabled}, any available
 245  
      * {@link #getCacheManager() cacheManager} will be used to acquire the cache instance if available.
 246  
      * <p/>
 247  
      * <b>WARNING:</b> Only set this property if safe caching conditions apply, as documented at the top
 248  
      * of this page in the class-level JavaDoc.
 249  
      *
 250  
      * @param authenticationCache an explicit {@link Cache} instance to use for authentication caching or
 251  
      *                            {@code null} if the cache should possibly be obtained another way.
 252  
      * @see #isAuthenticationCachingEnabled()
 253  
      * @since 1.2
 254  
      */
 255  
     public void setAuthenticationCache(Cache<Object, AuthenticationInfo> authenticationCache) {
 256  1
         this.authenticationCache = authenticationCache;
 257  1
     }
 258  
 
 259  
     /**
 260  
      * Returns a {@link Cache} instance to use for authentication caching, or {@code null} if no cache has been
 261  
      * set.
 262  
      *
 263  
      * @return a {@link Cache} instance to use for authentication caching, or {@code null} if no cache has been
 264  
      *         set.
 265  
      * @see #setAuthenticationCache(org.apache.shiro.cache.Cache)
 266  
      * @see #isAuthenticationCachingEnabled()
 267  
      * @since 1.2
 268  
      */
 269  
     public Cache<Object, AuthenticationInfo> getAuthenticationCache() {
 270  75
         return this.authenticationCache;
 271  
     }
 272  
 
 273  
     /**
 274  
      * Returns the name of a {@link Cache} to lookup from any available {@link #getCacheManager() cacheManager} if
 275  
      * a cache is not explicitly configured via {@link #setAuthenticationCache(org.apache.shiro.cache.Cache)}.
 276  
      * <p/>
 277  
      * This name will only be used to look up a cache if authentication caching is
 278  
      * {@link #isAuthenticationCachingEnabled() enabled}.
 279  
      * <p/>
 280  
      * <b>WARNING:</b> Only set this property if safe caching conditions apply, as documented at the top
 281  
      * of this page in the class-level JavaDoc.
 282  
      *
 283  
      * @return the name of a {@link Cache} to lookup from any available {@link #getCacheManager() cacheManager} if
 284  
      *         a cache is not explicitly configured via {@link #setAuthenticationCache(org.apache.shiro.cache.Cache)}.
 285  
      * @see #isAuthenticationCachingEnabled()
 286  
      * @since 1.2
 287  
      */
 288  
     public String getAuthenticationCacheName() {
 289  6
         return this.authenticationCacheName;
 290  
     }
 291  
 
 292  
     /**
 293  
      * Sets the name of a {@link Cache} to lookup from any available {@link #getCacheManager() cacheManager} if
 294  
      * a cache is not explicitly configured via {@link #setAuthenticationCache(org.apache.shiro.cache.Cache)}.
 295  
      * <p/>
 296  
      * This name will only be used to look up a cache if authentication caching is
 297  
      * {@link #isAuthenticationCachingEnabled() enabled}.
 298  
      *
 299  
      * @param authenticationCacheName the name of a {@link Cache} to lookup from any available
 300  
      *                                {@link #getCacheManager() cacheManager} if a cache is not explicitly configured
 301  
      *                                via {@link #setAuthenticationCache(org.apache.shiro.cache.Cache)}.
 302  
      * @see #isAuthenticationCachingEnabled()
 303  
      * @since 1.2
 304  
      */
 305  
     public void setAuthenticationCacheName(String authenticationCacheName) {
 306  1
         this.authenticationCacheName = authenticationCacheName;
 307  1
     }
 308  
 
 309  
     /**
 310  
      * Returns {@code true} if authentication caching should be utilized if a {@link CacheManager} has been
 311  
      * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) configured}, {@code false} otherwise.
 312  
      * <p/>
 313  
      * The default value is {@code true}.
 314  
      *
 315  
      * @return {@code true} if authentication caching should be utilized, {@code false} otherwise.
 316  
      */
 317  
     public boolean isAuthenticationCachingEnabled() {
 318  100
         return this.authenticationCachingEnabled && isCachingEnabled();
 319  
     }
 320  
 
 321  
     /**
 322  
      * Sets whether or not authentication caching should be utilized if a {@link CacheManager} has been
 323  
      * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) configured}, {@code false} otherwise.
 324  
      * <p/>
 325  
      * The default value is {@code false} to retain backwards compatibility with Shiro 1.1 and earlier.
 326  
      * <p/>
 327  
      * <b>WARNING:</b> Only set this property to {@code true} if safe caching conditions apply, as documented at the top
 328  
      * of this page in the class-level JavaDoc.
 329  
      *
 330  
      * @param authenticationCachingEnabled the value to set
 331  
      */
 332  
     @SuppressWarnings({"UnusedDeclaration"})
 333  
     public void setAuthenticationCachingEnabled(boolean authenticationCachingEnabled) {
 334  4
         this.authenticationCachingEnabled = authenticationCachingEnabled;
 335  4
         if (authenticationCachingEnabled) {
 336  4
             setCachingEnabled(true);
 337  
         }
 338  4
     }
 339  
 
 340  
     public void setName(String name) {
 341  23
         super.setName(name);
 342  23
         String authcCacheName = this.authenticationCacheName;
 343  23
         if (authcCacheName != null && authcCacheName.startsWith(getClass().getName())) {
 344  
             //get rid of the default heuristically-created cache name.  Create a more meaningful one
 345  
             //based on the application-unique Realm name:
 346  23
             this.authenticationCacheName = name + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
 347  
         }
 348  23
     }
 349  
 
 350  
 
 351  
     /*--------------------------------------------
 352  
     |               M E T H O D S               |
 353  
     ============================================*/
 354  
 
 355  
     /**
 356  
      * Convenience implementation that returns
 357  
      * <tt>getAuthenticationTokenClass().isAssignableFrom( token.getClass() );</tt>.  Can be overridden
 358  
      * by subclasses for more complex token checking.
 359  
      * <p>Most configurations will only need to set a different class via
 360  
      * {@link #setAuthenticationTokenClass}, as opposed to overriding this method.
 361  
      *
 362  
      * @param token the token being submitted for authentication.
 363  
      * @return true if this authentication realm can process the submitted token instance of the class, false otherwise.
 364  
      */
 365  
     public boolean supports(AuthenticationToken token) {
 366  23
         return token != null && getAuthenticationTokenClass().isAssignableFrom(token.getClass());
 367  
     }
 368  
 
 369  
     /**
 370  
      * Initializes this realm and potentially enables an authentication cache, depending on configuration.  Based on
 371  
      * the availability of an authentication cache, this class functions as follows:
 372  
      * <ol>
 373  
      * <li>If the {@link #setAuthenticationCache cache} property has been set, it will be
 374  
      * used to cache the AuthenticationInfo objects returned from {@link #getAuthenticationInfo}
 375  
      * method invocations.
 376  
      * All future calls to {@link #getAuthenticationInfo} will attempt to use this cache first
 377  
      * to alleviate any potentially unnecessary calls to an underlying data store.</li>
 378  
      * <li>If the {@link #setAuthenticationCache cache} property has <b>not</b> been set,
 379  
      * the {@link #setCacheManager cacheManager} property will be checked.
 380  
      * If a {@code cacheManager} has been set, it will be used to eagerly acquire an authentication
 381  
      * {@code cache}, and this cache which will be used as specified in #1.</li>
 382  
      * <li>If neither the {@link #setAuthenticationCache (org.apache.shiro.cache.Cache) authenticationCache}
 383  
      * or {@link #setCacheManager(org.apache.shiro.cache.CacheManager) cacheManager}
 384  
      * properties are set, caching will not be utilized and authentication look-ups will be delegated to
 385  
      * subclass implementations for each authentication attempt.</li>
 386  
      * </ol>
 387  
      * <p/>
 388  
      * This method finishes by calling {@link #onInit()} is to allow subclasses to perform any init behavior desired.
 389  
      *
 390  
      * @since 1.2
 391  
      */
 392  
     public final void init() {
 393  
         //trigger obtaining the authorization cache if possible
 394  25
         getAvailableAuthenticationCache();
 395  25
         onInit();
 396  24
     }
 397  
 
 398  
     /**
 399  
      * Template method for subclasses to implement any initialization logic.  Called from
 400  
      * {@link #init()}.
 401  
      *
 402  
      * @since 1.2
 403  
      */
 404  
     protected void onInit() {
 405  30
     }
 406  
 
 407  
     /**
 408  
      * This implementation attempts to acquire an authentication cache if one is not already configured.
 409  
      *
 410  
      * @since 1.2
 411  
      */
 412  
     protected void afterCacheManagerSet() {
 413  
         //trigger obtaining the authorization cache if possible
 414  6
         getAvailableAuthenticationCache();
 415  6
     }
 416  
 
 417  
     /**
 418  
      * Returns any available {@link Cache} instance to use for authentication caching.  This functions as follows:
 419  
      * <ol>
 420  
      * <li>If an {@link #setAuthenticationCache(org.apache.shiro.cache.Cache) authenticationCache} has been explicitly
 421  
      * configured (it is not null), it is returned.</li>
 422  
      * <li>If there is no {@link #getAuthenticationCache() authenticationCache} configured:
 423  
      * <ol>
 424  
      * <li>If authentication caching is {@link #isAuthenticationCachingEnabled() enabled}, any available
 425  
      * {@link #getCacheManager() cacheManager} will be consulted to obtain an available authentication cache.
 426  
      * </li>
 427  
      * <li>If authentication caching is disabled, this implementation does nothing.</li>
 428  
      * </ol>
 429  
      * </li>
 430  
      * </ol>
 431  
      *
 432  
      * @return any available {@link Cache} instance to use for authentication caching.
 433  
      */
 434  
     private Cache<Object, AuthenticationInfo> getAvailableAuthenticationCache() {
 435  74
         Cache<Object, AuthenticationInfo> cache = getAuthenticationCache();
 436  74
         boolean authcCachingEnabled = isAuthenticationCachingEnabled();
 437  74
         if (cache == null && authcCachingEnabled) {
 438  4
             cache = getAuthenticationCacheLazy();
 439  
         }
 440  74
         return cache;
 441  
     }
 442  
 
 443  
     /**
 444  
      * Checks to see if the authenticationCache class attribute is null, and if so, attempts to acquire one from
 445  
      * any configured {@link #getCacheManager() cacheManager}.  If one is acquired, it is set as the class attribute.
 446  
      * The class attribute is then returned.
 447  
      *
 448  
      * @return an available cache instance to be used for authentication caching or {@code null} if one is not available.
 449  
      * @since 1.2
 450  
      */
 451  
     private Cache<Object, AuthenticationInfo> getAuthenticationCacheLazy() {
 452  
 
 453  4
         if (this.authenticationCache == null) {
 454  
 
 455  4
             log.trace("No authenticationCache instance set.  Checking for a cacheManager...");
 456  
 
 457  4
             CacheManager cacheManager = getCacheManager();
 458  
 
 459  4
             if (cacheManager != null) {
 460  4
                 String cacheName = getAuthenticationCacheName();
 461  4
                 log.debug("CacheManager [{}] configured.  Building authentication cache '{}'", cacheManager, cacheName);
 462  4
                 this.authenticationCache = cacheManager.getCache(cacheName);
 463  
             }
 464  
         }
 465  
 
 466  4
         return this.authenticationCache;
 467  
     }
 468  
 
 469  
     /**
 470  
      * Returns any cached AuthenticationInfo corresponding to the specified token or {@code null} if there currently
 471  
      * isn't any cached data.
 472  
      *
 473  
      * @param token the token submitted during the authentication attempt.
 474  
      * @return any cached AuthenticationInfo corresponding to the specified token or {@code null} if there currently
 475  
      *         isn't any cached data.
 476  
      * @since 1.2
 477  
      */
 478  
     private AuthenticationInfo getCachedAuthenticationInfo(AuthenticationToken token) {
 479  32
         AuthenticationInfo info = null;
 480  
 
 481  32
         Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
 482  32
         if (cache != null && token != null) {
 483  4
             log.trace("Attempting to retrieve the AuthenticationInfo from cache.");
 484  4
             Object key = getAuthenticationCacheKey(token);
 485  4
             info = cache.get(key);
 486  4
             if (info == null) {
 487  2
                 log.trace("No AuthorizationInfo found in cache for key [{}]", key);
 488  
             } else {
 489  2
                 log.trace("Found cached AuthorizationInfo for key [{}]", key);
 490  
             }
 491  
         }
 492  
 
 493  32
         return info;
 494  
     }
 495  
 
 496  
     /**
 497  
      * Caches the specified info if authentication caching
 498  
      * {@link #isAuthenticationCachingEnabled(org.apache.shiro.authc.AuthenticationToken, org.apache.shiro.authc.AuthenticationInfo) isEnabled}
 499  
      * for the specific token/info pair and a cache instance is available to be used.
 500  
      *
 501  
      * @param token the authentication token submitted which resulted in a successful authentication attempt.
 502  
      * @param info  the AuthenticationInfo to cache as a result of the successful authentication attempt.
 503  
      * @since 1.2
 504  
      */
 505  
     private void cacheAuthenticationInfoIfPossible(AuthenticationToken token, AuthenticationInfo info) {
 506  26
         if (!isAuthenticationCachingEnabled(token, info)) {
 507  24
             log.debug("AuthenticationInfo caching is disabled for info [{}].  Submitted token: [{}].", info, token);
 508  
             //return quietly, caching is disabled for this token/info pair:
 509  24
             return;
 510  
         }
 511  
 
 512  2
         Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
 513  2
         if (cache != null) {
 514  2
             Object key = getAuthenticationCacheKey(token);
 515  2
             cache.put(key, info);
 516  2
             log.trace("Cached AuthenticationInfo for continued authentication.  key=[{}], value=[{}].", key, info);
 517  
         }
 518  2
     }
 519  
 
 520  
     /**
 521  
      * Returns {@code true} if authentication caching should be utilized based on the specified
 522  
      * {@link AuthenticationToken} and/or {@link AuthenticationInfo}, {@code false} otherwise.
 523  
      * <p/>
 524  
      * The default implementation simply delegates to {@link #isAuthenticationCachingEnabled()}, the general-case
 525  
      * authentication caching setting.  Subclasses can override this to turn on or off caching at runtime
 526  
      * based on the specific submitted runtime values.
 527  
      *
 528  
      * @param token the submitted authentication token
 529  
      * @param info  the {@code AuthenticationInfo} acquired from data source lookup via
 530  
      *              {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)}
 531  
      * @return {@code true} if authentication caching should be utilized based on the specified
 532  
      *         {@link AuthenticationToken} and/or {@link AuthenticationInfo}, {@code false} otherwise.
 533  
      * @since 1.2
 534  
      */
 535  
     protected boolean isAuthenticationCachingEnabled(AuthenticationToken token, AuthenticationInfo info) {
 536  26
         return isAuthenticationCachingEnabled();
 537  
     }
 538  
 
 539  
     /**
 540  
      * This implementation functions as follows:
 541  
      * <ol>
 542  
      * <li>It attempts to acquire any cached {@link AuthenticationInfo} corresponding to the specified
 543  
      * {@link AuthenticationToken} argument.  If a cached value is found, it will be used for credentials matching,
 544  
      * alleviating the need to perform any lookups with a data source.</li>
 545  
      * <li>If there is no cached {@link AuthenticationInfo} found, delegate to the
 546  
      * {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)} method to perform the actual
 547  
      * lookup.  If authentication caching is enabled and possible, any returned info object will be
 548  
      * {@link #cacheAuthenticationInfoIfPossible(org.apache.shiro.authc.AuthenticationToken, org.apache.shiro.authc.AuthenticationInfo) cached}
 549  
      * to be used in future authentication attempts.</li>
 550  
      * <li>If an AuthenticationInfo instance is not found in the cache or by lookup, {@code null} is returned to
 551  
      * indicate an account cannot be found.</li>
 552  
      * <li>If an AuthenticationInfo instance is found (either cached or via lookup), ensure the submitted
 553  
      * AuthenticationToken's credentials match the expected {@code AuthenticationInfo}'s credentials using the
 554  
      * {@link #getCredentialsMatcher() credentialsMatcher}.  This means that credentials are always verified
 555  
      * for an authentication attempt.</li>
 556  
      * </ol>
 557  
      *
 558  
      * @param token the submitted account principal and credentials.
 559  
      * @return the AuthenticationInfo corresponding to the given {@code token}, or {@code null} if no
 560  
      *         AuthenticationInfo could be found.
 561  
      * @throws AuthenticationException if authentication failed.
 562  
      */
 563  
     public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
 564  
 
 565  32
         AuthenticationInfo info = getCachedAuthenticationInfo(token);
 566  32
         if (info == null) {
 567  
             //otherwise not cached, perform the lookup:
 568  30
             info = doGetAuthenticationInfo(token);
 569  27
             log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
 570  27
             if (token != null && info != null) {
 571  26
                 cacheAuthenticationInfoIfPossible(token, info);
 572  
             }
 573  
         } else {
 574  2
             log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
 575  
         }
 576  
 
 577  29
         if (info != null) {
 578  28
             assertCredentialsMatch(token, info);
 579  
         } else {
 580  1
             log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
 581  
         }
 582  
 
 583  26
         return info;
 584  
     }
 585  
 
 586  
     /**
 587  
      * Asserts that the submitted {@code AuthenticationToken}'s credentials match the stored account
 588  
      * {@code AuthenticationInfo}'s credentials, and if not, throws an {@link AuthenticationException}.
 589  
      *
 590  
      * @param token the submitted authentication token
 591  
      * @param info  the AuthenticationInfo corresponding to the given {@code token}
 592  
      * @throws AuthenticationException if the token's credentials do not match the stored account credentials.
 593  
      */
 594  
     protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
 595  30
         CredentialsMatcher cm = getCredentialsMatcher();
 596  30
         if (cm != null) {
 597  29
             if (!cm.doCredentialsMatch(token, info)) {
 598  
                 //not successful - throw an exception to indicate this:
 599  4
                 String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
 600  4
                 throw new IncorrectCredentialsException(msg);
 601  
             }
 602  
         } else {
 603  1
             throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
 604  
                     "credentials during authentication.  If you do not wish for credentials to be examined, you " +
 605  
                     "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
 606  
         }
 607  25
     }
 608  
 
 609  
     /**
 610  
      * Returns the key under which {@link AuthenticationInfo} instances are cached if authentication caching is enabled.
 611  
      * This implementation defaults to returning the token's
 612  
      * {@link org.apache.shiro.authc.AuthenticationToken#getPrincipal() principal}, which is usually a username in
 613  
      * most applications.
 614  
      * <h3>Cache Invalidation on Logout</h3>
 615  
      * <b>NOTE:</b> If you want to be able to invalidate an account's cached {@code AuthenticationInfo} on logout, you
 616  
      * must ensure the {@link #getAuthenticationCacheKey(org.apache.shiro.subject.PrincipalCollection)} method returns
 617  
      * the same value as this method.
 618  
      *
 619  
      * @param token the authentication token for which any successful authentication will be cached.
 620  
      * @return the cache key to use to cache the associated {@link AuthenticationInfo} after a successful authentication.
 621  
      * @since 1.2
 622  
      */
 623  
     protected Object getAuthenticationCacheKey(AuthenticationToken token) {
 624  6
         return token != null ? token.getPrincipal() : null;
 625  
     }
 626  
 
 627  
     /**
 628  
      * Returns the key under which {@link AuthenticationInfo} instances are cached if authentication caching is enabled.
 629  
      * This implementation delegates to
 630  
      * {@link #getAvailablePrincipal(org.apache.shiro.subject.PrincipalCollection)}, which returns the primary principal
 631  
      * associated with this particular Realm.
 632  
      * <h3>Cache Invalidation on Logout</h3>
 633  
      * <b>NOTE:</b> If you want to be able to invalidate an account's cached {@code AuthenticationInfo} on logout, you
 634  
      * must ensure that this method returns the same value as the
 635  
      * {@link #getAuthenticationCacheKey(org.apache.shiro.authc.AuthenticationToken)} method!
 636  
      *
 637  
      * @param principals the principals of the account for which to set or remove cached {@code AuthenticationInfo}.
 638  
      * @return the cache key to use when looking up cached {@link AuthenticationInfo} instances.
 639  
      * @since 1.2
 640  
      */
 641  
     protected Object getAuthenticationCacheKey(PrincipalCollection principals) {
 642  1
         return getAvailablePrincipal(principals);
 643  
     }
 644  
 
 645  
     /**
 646  
      * This implementation clears out any cached authentication data by calling
 647  
      * {@link #clearCachedAuthenticationInfo(org.apache.shiro.subject.PrincipalCollection)}.
 648  
      * If overriding in a subclass, be sure to call {@code super.doClearCache} to ensure this behavior is maintained.
 649  
      *
 650  
      * @param principals principals the principals of the account for which to clear any cached data.
 651  
      * @since 1.2
 652  
      */
 653  
     @Override
 654  
     protected void doClearCache(PrincipalCollection principals) {
 655  9
         super.doClearCache(principals);
 656  9
         clearCachedAuthenticationInfo(principals);
 657  9
     }
 658  
 
 659  
     /**
 660  
      * Clears out the AuthenticationInfo cache entry for the specified account.
 661  
      * <p/>
 662  
      * This method is provided as a convenience to subclasses so they can invalidate a cache entry when they
 663  
      * change an account's authentication data (e.g. reset password) during runtime.  Because an account's
 664  
      * AuthenticationInfo can be cached, there needs to be a way to invalidate the cache for only that account so that
 665  
      * subsequent authentication operations don't used the (old) cached value if account data changes.
 666  
      * <p/>
 667  
      * After this method is called, the next authentication for that same account will result in a call to
 668  
      * {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) doGetAuthenticationInfo}, and the
 669  
      * resulting return value will be cached before being returned so it can be reused for later authentications.
 670  
      * <p/>
 671  
      * If you wish to clear out all associated cached data (and not just authentication data), use the
 672  
      * {@link #clearCache(org.apache.shiro.subject.PrincipalCollection)} method instead (which will in turn call this
 673  
      * method by default).
 674  
      *
 675  
      * @param principals the principals of the account for which to clear the cached AuthorizationInfo.
 676  
      * @see #clearCache(org.apache.shiro.subject.PrincipalCollection)
 677  
      * @since 1.2
 678  
      */
 679  
     protected void clearCachedAuthenticationInfo(PrincipalCollection principals) {
 680  9
         if (!CollectionUtils.isEmpty(principals)) {
 681  9
             Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
 682  
             //cache instance will be non-null if caching is enabled:
 683  9
             if (cache != null) {
 684  1
                 Object key = getAuthenticationCacheKey(principals);
 685  1
                 cache.remove(key);
 686  
             }
 687  
         }
 688  9
     }
 689  
 
 690  
     /**
 691  
      * Retrieves authentication data from an implementation-specific datasource (RDBMS, LDAP, etc) for the given
 692  
      * authentication token.
 693  
      * <p/>
 694  
      * For most datasources, this means just 'pulling' authentication data for an associated subject/user and nothing
 695  
      * more and letting Shiro do the rest.  But in some systems, this method could actually perform EIS specific
 696  
      * log-in logic in addition to just retrieving data - it is up to the Realm implementation.
 697  
      * <p/>
 698  
      * A {@code null} return value means that no account could be associated with the specified token.
 699  
      *
 700  
      * @param token the authentication token containing the user's principal and credentials.
 701  
      * @return an {@link AuthenticationInfo} object containing account data resulting from the
 702  
      *         authentication ONLY if the lookup is successful (i.e. account exists and is valid, etc.)
 703  
      * @throws AuthenticationException if there is an error acquiring data or performing
 704  
      *                                 realm-specific authentication logic for the specified <tt>token</tt>
 705  
      */
 706  
     protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
 707  
 
 708  
 }