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