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