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.realm;
020    
021    import org.apache.shiro.authc.*;
022    import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
023    import org.apache.shiro.authc.credential.CredentialsMatcher;
024    import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
025    import org.apache.shiro.cache.Cache;
026    import org.apache.shiro.cache.CacheManager;
027    import org.apache.shiro.subject.PrincipalCollection;
028    import org.apache.shiro.util.CollectionUtils;
029    import org.apache.shiro.util.Initializable;
030    import org.slf4j.Logger;
031    import org.slf4j.LoggerFactory;
032    
033    import java.util.concurrent.atomic.AtomicInteger;
034    
035    
036    /**
037     * A top-level abstract implementation of the <tt>Realm</tt> interface that only implements authentication support
038     * (log-in) operations and leaves authorization (access control) behavior to subclasses.
039     * <h2>Authentication Caching</h2>
040     * For applications that perform frequent repeated authentication of the same accounts (e.g. as is often done in
041     * REST or Soap applications that authenticate on every request), it might be prudent to enable authentication
042     * caching to alleviate constant load on any back-end data sources.
043     * <p/>
044     * This feature is disabled by default to retain backwards-compatibility with Shiro 1.1 and earlier.  It may be
045     * enabled by setting {@link #setAuthenticationCachingEnabled(boolean) authenticationCachingEnabled} = {@code true}
046     * (and configuring Shiro with a {@link CacheManager} of course), but <b>NOTE:</b>
047     * <p/>
048     * <b>ONLY enable authentication caching if either of the following is true for your realm implementation:</b>
049     * <ul>
050     * <li>The {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) doGetAuthenticationInfo}
051     * implementation returns {@code AuthenticationInfo} instances where the
052     * {@link org.apache.shiro.authc.AuthenticationInfo#getCredentials() credentials} are securely obfuscated and NOT
053     * plaintext (raw) credentials. For example,
054     * if your realm references accounts with passwords, that the {@code AuthenticationInfo}'s
055     * {@link org.apache.shiro.authc.AuthenticationInfo#getCredentials() credentials} are safely hashed and salted or otherwise
056     * fully encrypted.<br/><br/></li>
057     * <li>The {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) doGetAuthenticationInfo}
058     * implementation returns {@code AuthenticationInfo} instances where the
059     * {@link org.apache.shiro.authc.AuthenticationInfo#getCredentials() credentials} are plaintext (raw) <b>AND</b> the
060     * cache region storing the {@code AuthenticationInfo} instances WILL NOT overflow to disk and WILL NOT transmit cache
061     * entries over an unprotected (non TLS/SSL) network (as might be the case with a networked/distributed enterprise cache).
062     * This should be the case even in private/trusted/corporate networks.</li>
063     * </ul>
064     * <p/>
065     * These points are very important because if authentication caching is enabled, this abstract class implementation
066     * will place AuthenticationInfo instances returned from the subclass implementations directly into the cache, for
067     * example:
068     * <pre>
069     * cache.put(cacheKey, subclassAuthenticationInfoInstance);
070     * </pre>
071     * <p/>
072     * Enabling authentication caching is ONLY safe to do if the above two scenarios apply.  It is NOT safe to enable under
073     * any other scenario.
074     * <p/>
075     * When possible, always represent and store credentials in a safe form (hash+salt or encrypted) to eliminate plaintext
076     * visibility.
077     * <h3>Authentication Cache Invalidation on Logout</h3>
078     * If authentication caching is enabled, this implementation will attempt to evict (remove) cached authentication data
079     * for an account during logout.  This can only occur if the
080     * {@link #getAuthenticationCacheKey(org.apache.shiro.authc.AuthenticationToken)} and
081     * {@link #getAuthenticationCacheKey(org.apache.shiro.subject.PrincipalCollection)} methods return the exact same value.
082     * <p/>
083     * The default implementations of these methods expect that the
084     * {@link org.apache.shiro.authc.AuthenticationToken#getPrincipal()} (what the user submits during login) and
085     * {@link #getAvailablePrincipal(org.apache.shiro.subject.PrincipalCollection) getAvailablePrincipal} (what is returned
086     * by the realm after account lookup) return
087     * the same exact value.  For example, the user submitted username is also the primary account identifier.
088     * <p/>
089     * However, if your application uses, say, a username for end-user login, but returns a primary key ID as the
090     * primary principal after authentication, then you will need to override either
091     * {@link #getAuthenticationCacheKey(org.apache.shiro.authc.AuthenticationToken) getAuthenticationCacheKey(token)} or
092     * {@link #getAuthenticationCacheKey(org.apache.shiro.subject.PrincipalCollection) getAuthenticationCacheKey(principals)}
093     * (or both) to ensure that the same cache key can be used for either object.
094     * <p/>
095     * This guarantees that the same cache key used to cache the data during authentication (derived from the
096     * {@code AuthenticationToken}) will be used to remove the cached data during logout (derived from the
097     * {@code PrincipalCollection}).
098     * <h4>Unmatching Cache Key Values</h4>
099     * 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        private static final Logger log = LoggerFactory.getLogger(AuthenticatingRealm.class);
115    
116        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            this(null, new SimpleCredentialsMatcher());
148        }
149    
150        public AuthenticatingRealm(CacheManager cacheManager) {
151            this(cacheManager, new SimpleCredentialsMatcher());
152        }
153    
154        public AuthenticatingRealm(CredentialsMatcher matcher) {
155            this(null, matcher);
156        }
157    
158        public AuthenticatingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
159            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            this.authenticationCachingEnabled = false;
164    
165            int instanceNumber = INSTANCE_COUNT.getAndIncrement();
166            this.authenticationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
167            if (instanceNumber > 0) {
168                this.authenticationCacheName = this.authenticationCacheName + "." + instanceNumber;
169            }
170    
171            if (cacheManager != null) {
172                setCacheManager(cacheManager);
173            }
174            if (matcher != null) {
175                setCredentialsMatcher(matcher);
176            }
177        }
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            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            this.credentialsMatcher = credentialsMatcher;
209        }
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            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            this.authenticationTokenClass = authenticationTokenClass;
240        }
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            this.authenticationCache = authenticationCache;
257        }
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            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            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            this.authenticationCacheName = authenticationCacheName;
307        }
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            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            this.authenticationCachingEnabled = authenticationCachingEnabled;
335            if (authenticationCachingEnabled) {
336                setCachingEnabled(true);
337            }
338        }
339    
340        public void setName(String name) {
341            super.setName(name);
342            String authcCacheName = this.authenticationCacheName;
343            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                this.authenticationCacheName = name + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
347            }
348        }
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            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            getAvailableAuthenticationCache();
395            onInit();
396        }
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        }
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            getAvailableAuthenticationCache();
415        }
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            Cache<Object, AuthenticationInfo> cache = getAuthenticationCache();
436            boolean authcCachingEnabled = isAuthenticationCachingEnabled();
437            if (cache == null && authcCachingEnabled) {
438                cache = getAuthenticationCacheLazy();
439            }
440            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            if (this.authenticationCache == null) {
454    
455                log.trace("No authenticationCache instance set.  Checking for a cacheManager...");
456    
457                CacheManager cacheManager = getCacheManager();
458    
459                if (cacheManager != null) {
460                    String cacheName = getAuthenticationCacheName();
461                    log.debug("CacheManager [{}] configured.  Building authentication cache '{}'", cacheManager, cacheName);
462                    this.authenticationCache = cacheManager.getCache(cacheName);
463                }
464            }
465    
466            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            AuthenticationInfo info = null;
480    
481            Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
482            if (cache != null && token != null) {
483                log.trace("Attempting to retrieve the AuthenticationInfo from cache.");
484                Object key = getAuthenticationCacheKey(token);
485                info = cache.get(key);
486                if (info == null) {
487                    log.trace("No AuthorizationInfo found in cache for key [{}]", key);
488                } else {
489                    log.trace("Found cached AuthorizationInfo for key [{}]", key);
490                }
491            }
492    
493            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            if (!isAuthenticationCachingEnabled(token, info)) {
507                log.debug("AuthenticationInfo caching is disabled for info [{}].  Submitted token: [{}].", info, token);
508                //return quietly, caching is disabled for this token/info pair:
509                return;
510            }
511    
512            Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
513            if (cache != null) {
514                Object key = getAuthenticationCacheKey(token);
515                cache.put(key, info);
516                log.trace("Cached AuthenticationInfo for continued authentication.  key=[{}], value=[{}].", key, info);
517            }
518        }
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            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            AuthenticationInfo info = getCachedAuthenticationInfo(token);
566            if (info == null) {
567                //otherwise not cached, perform the lookup:
568                info = doGetAuthenticationInfo(token);
569                log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
570                if (token != null && info != null) {
571                    cacheAuthenticationInfoIfPossible(token, info);
572                }
573            } else {
574                log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
575            }
576    
577            if (info != null) {
578                assertCredentialsMatch(token, info);
579            } else {
580                log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
581            }
582    
583            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            CredentialsMatcher cm = getCredentialsMatcher();
596            if (cm != null) {
597                if (!cm.doCredentialsMatch(token, info)) {
598                    //not successful - throw an exception to indicate this:
599                    String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
600                    throw new IncorrectCredentialsException(msg);
601                }
602            } else {
603                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        }
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            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            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            super.doClearCache(principals);
656            clearCachedAuthenticationInfo(principals);
657        }
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            if (!CollectionUtils.isEmpty(principals)) {
681                Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
682                //cache instance will be non-null if caching is enabled:
683                if (cache != null) {
684                    Object key = getAuthenticationCacheKey(principals);
685                    cache.remove(key);
686                }
687            }
688        }
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    }