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 }