View Javadoc

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     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 }