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