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 }