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.mgt; 020 021import org.apache.shiro.authc.AuthenticationException; 022import org.apache.shiro.authc.AuthenticationInfo; 023import org.apache.shiro.authc.AuthenticationToken; 024import org.apache.shiro.authc.Authenticator; 025import org.apache.shiro.authc.LogoutAware; 026import org.apache.shiro.authz.Authorizer; 027import org.apache.shiro.realm.Realm; 028import org.apache.shiro.session.InvalidSessionException; 029import org.apache.shiro.session.Session; 030import org.apache.shiro.session.mgt.DefaultSessionContext; 031import org.apache.shiro.session.mgt.DefaultSessionKey; 032import org.apache.shiro.session.mgt.SessionContext; 033import org.apache.shiro.session.mgt.SessionKey; 034import org.apache.shiro.subject.PrincipalCollection; 035import org.apache.shiro.subject.Subject; 036import org.apache.shiro.subject.SubjectContext; 037import org.apache.shiro.subject.support.DefaultSubjectContext; 038import org.apache.shiro.util.CollectionUtils; 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041 042import java.io.Serializable; 043import java.util.Collection; 044 045/** 046 * The Shiro framework's default concrete implementation of the {@link SecurityManager} interface, 047 * based around a collection of {@link org.apache.shiro.realm.Realm}s. This implementation delegates its 048 * authentication, authorization, and session operations to wrapped {@link Authenticator}, {@link Authorizer}, and 049 * {@link org.apache.shiro.session.mgt.SessionManager SessionManager} instances respectively via superclass 050 * implementation. 051 * <p/> 052 * To greatly reduce and simplify configuration, this implementation (and its superclasses) will 053 * create suitable defaults for all of its required dependencies, <em>except</em> the required one or more 054 * {@link Realm Realm}s. Because {@code Realm} implementations usually interact with an application's data model, 055 * they are almost always application specific; you will want to specify at least one custom 056 * {@code Realm} implementation that 'knows' about your application's data/security model 057 * (via {@link #setRealm} or one of the overloaded constructors). All other attributes in this class hierarchy 058 * will have suitable defaults for most enterprise applications. 059 * <p/> 060 * <b>RememberMe notice</b>: This class supports the ability to configure a 061 * {@link #setRememberMeManager RememberMeManager} 062 * for {@code RememberMe} identity services for login/logout, BUT, a default instance <em>will not</em> be created 063 * for this attribute at startup. 064 * <p/> 065 * Because RememberMe services are inherently client tier-specific and 066 * therefore application-dependent, if you want {@code RememberMe} services enabled, you will have to specify an 067 * instance yourself via the {@link #setRememberMeManager(RememberMeManager) setRememberMeManager} 068 * mutator. However if you're reading this JavaDoc with the 069 * expectation of operating in a Web environment, take a look at the 070 * {@code org.apache.shiro.web.DefaultWebSecurityManager} implementation, which 071 * <em>does</em> support {@code RememberMe} services by default at startup. 072 * 073 * @since 0.2 074 */ 075@SuppressWarnings("checkstyle:MethodCount") 076public class DefaultSecurityManager extends SessionsSecurityManager { 077 078 private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSecurityManager.class); 079 080 protected RememberMeManager rememberMeManager; 081 protected SubjectDAO subjectDAO; 082 protected SubjectFactory subjectFactory; 083 084 /** 085 * Default no-arg constructor. 086 */ 087 public DefaultSecurityManager() { 088 super(); 089 this.subjectFactory = new DefaultSubjectFactory(); 090 this.subjectDAO = new DefaultSubjectDAO(); 091 } 092 093 /** 094 * Supporting constructor for a single-realm application. 095 * 096 * @param singleRealm the single realm used by this SecurityManager. 097 */ 098 public DefaultSecurityManager(Realm singleRealm) { 099 this(); 100 setRealm(singleRealm); 101 } 102 103 /** 104 * Supporting constructor for multiple {@link #setRealms realms}. 105 * 106 * @param realms the realm instances backing this SecurityManager. 107 */ 108 public DefaultSecurityManager(Collection<Realm> realms) { 109 this(); 110 setRealms(realms); 111 } 112 113 /** 114 * Returns the {@code SubjectFactory} responsible for creating {@link Subject} instances exposed to the application. 115 * 116 * @return the {@code SubjectFactory} responsible for creating {@link Subject} instances exposed to the application. 117 */ 118 public SubjectFactory getSubjectFactory() { 119 return subjectFactory; 120 } 121 122 /** 123 * Sets the {@code SubjectFactory} responsible for creating {@link Subject} instances exposed to the application. 124 * 125 * @param subjectFactory the {@code SubjectFactory} responsible for creating 126 * {@link Subject} instances exposed to the application. 127 */ 128 public void setSubjectFactory(SubjectFactory subjectFactory) { 129 this.subjectFactory = subjectFactory; 130 } 131 132 /** 133 * Returns the {@code SubjectDAO} responsible for persisting Subject state, typically used after login or when an 134 * Subject identity is discovered (e.g. after RememberMe services). Unless configured otherwise, the default 135 * implementation is a {@link DefaultSubjectDAO}. 136 * 137 * @return the {@code SubjectDAO} responsible for persisting Subject state, typically used after login or when an 138 * Subject identity is discovered (e.g. after RememberMe services). 139 * @see DefaultSubjectDAO 140 * @since 1.2 141 */ 142 public SubjectDAO getSubjectDAO() { 143 return subjectDAO; 144 } 145 146 /** 147 * Sets the {@code SubjectDAO} responsible for persisting Subject state, typically used after login or when an 148 * Subject identity is discovered (e.g. after RememberMe services). Unless configured otherwise, the default 149 * implementation is a {@link DefaultSubjectDAO}. 150 * 151 * @param subjectDAO the {@code SubjectDAO} responsible for persisting Subject state, typically used after login or when an 152 * Subject identity is discovered (e.g. after RememberMe services). 153 * @see DefaultSubjectDAO 154 * @since 1.2 155 */ 156 public void setSubjectDAO(SubjectDAO subjectDAO) { 157 this.subjectDAO = subjectDAO; 158 } 159 160 public RememberMeManager getRememberMeManager() { 161 return rememberMeManager; 162 } 163 164 public void setRememberMeManager(RememberMeManager rememberMeManager) { 165 this.rememberMeManager = rememberMeManager; 166 } 167 168 protected SubjectContext createSubjectContext() { 169 return new DefaultSubjectContext(); 170 } 171 172 /** 173 * Creates a {@code Subject} instance for the user represented by the given method arguments. 174 * 175 * @param token the {@code AuthenticationToken} submitted for the successful authentication. 176 * @param info the {@code AuthenticationInfo} of a newly authenticated user. 177 * @param existing the existing {@code Subject} instance that initiated the authentication attempt 178 * @return the {@code Subject} instance that represents the context and session data for the newly 179 * authenticated subject. 180 */ 181 protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) { 182 SubjectContext context = createSubjectContext(); 183 context.setAuthenticated(true); 184 context.setAuthenticationToken(token); 185 context.setAuthenticationInfo(info); 186 context.setSecurityManager(this); 187 if (existing != null) { 188 context.setSubject(existing); 189 } 190 return createSubject(context); 191 } 192 193 /** 194 * Binds a {@code Subject} instance created after authentication to the application for later use. 195 * <p/> 196 * As of Shiro 1.2, this method has been deprecated in favor of {@link #save(org.apache.shiro.subject.Subject)}, 197 * which this implementation now calls. 198 * 199 * @param subject the {@code Subject} instance created after authentication to be bound to the application 200 * for later use. 201 * @see #save(org.apache.shiro.subject.Subject) 202 * @deprecated in favor of {@link #save(org.apache.shiro.subject.Subject) save(subject)}. 203 */ 204 @Deprecated 205 protected void bind(Subject subject) { 206 save(subject); 207 } 208 209 protected void rememberMeSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) { 210 RememberMeManager rmm = getRememberMeManager(); 211 if (rmm != null) { 212 try { 213 rmm.onSuccessfulLogin(subject, token, info); 214 } catch (Exception e) { 215 if (LOGGER.isWarnEnabled()) { 216 String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() 217 + "] threw an exception during onSuccessfulLogin. RememberMe services will not be " 218 + "performed for account [" + info + "]."; 219 LOGGER.warn(msg, e); 220 } 221 } 222 } else { 223 if (LOGGER.isTraceEnabled()) { 224 LOGGER.trace("This " + getClass().getName() + " instance does not have a " 225 + "[" + RememberMeManager.class.getName() + "] instance configured. RememberMe services " 226 + "will not be performed for account [" + info + "]."); 227 } 228 } 229 } 230 231 protected void rememberMeFailedLogin(AuthenticationToken token, AuthenticationException ex, Subject subject) { 232 RememberMeManager rmm = getRememberMeManager(); 233 if (rmm != null) { 234 try { 235 rmm.onFailedLogin(subject, token, ex); 236 } catch (Exception e) { 237 if (LOGGER.isWarnEnabled()) { 238 String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() 239 + "] threw an exception during onFailedLogin for AuthenticationToken [" 240 + token + "]."; 241 LOGGER.warn(msg, e); 242 } 243 } 244 } 245 } 246 247 protected void rememberMeLogout(Subject subject) { 248 RememberMeManager rmm = getRememberMeManager(); 249 if (rmm != null) { 250 try { 251 rmm.onLogout(subject); 252 } catch (Exception e) { 253 if (LOGGER.isWarnEnabled()) { 254 String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() 255 + "] threw an exception during onLogout for subject with principals [" 256 + (subject != null ? subject.getPrincipals() : null) + "]"; 257 LOGGER.warn(msg, e); 258 } 259 } 260 } 261 } 262 263 /** 264 * First authenticates the {@code AuthenticationToken} argument, and if successful, constructs a 265 * {@code Subject} instance representing the authenticated account's identity. 266 * <p/> 267 * Once constructed, the {@code Subject} instance is then {@link #bind bound} to the application for 268 * subsequent access before being returned to the caller. 269 * 270 * @param token the authenticationToken to process for the login attempt. 271 * @return a Subject representing the authenticated user. 272 * @throws AuthenticationException if there is a problem authenticating the specified {@code token}. 273 */ 274 public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException { 275 AuthenticationInfo info; 276 try { 277 info = authenticate(token); 278 } catch (AuthenticationException ae) { 279 try { 280 onFailedLogin(token, ae, subject); 281 } catch (Exception e) { 282 if (LOGGER.isInfoEnabled()) { 283 LOGGER.info("onFailedLogin method threw an " 284 + "exception. Logging and propagating original AuthenticationException.", e); 285 } 286 } 287 //propagate 288 throw ae; 289 } 290 291 Subject loggedIn = createSubject(token, info, subject); 292 293 onSuccessfulLogin(token, info, loggedIn); 294 295 return loggedIn; 296 } 297 298 protected void onSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) { 299 rememberMeSuccessfulLogin(token, info, subject); 300 } 301 302 protected void onFailedLogin(AuthenticationToken token, AuthenticationException ae, Subject subject) { 303 rememberMeFailedLogin(token, ae, subject); 304 } 305 306 protected void beforeLogout(Subject subject) { 307 rememberMeLogout(subject); 308 } 309 310 protected SubjectContext copy(SubjectContext subjectContext) { 311 return new DefaultSubjectContext(subjectContext); 312 } 313 314 /** 315 * This implementation functions as follows: 316 * <p/> 317 * <ol> 318 * <li>Ensures the {@code SubjectContext} is as populated as it can be, using heuristics to acquire 319 * data that may not have already been available to it (such as a referenced session or remembered principals).</li> 320 * <li>Calls {@link #doCreateSubject(org.apache.shiro.subject.SubjectContext)} to actually perform the 321 * {@code Subject} instance creation.</li> 322 * <li>calls {@link #save(org.apache.shiro.subject.Subject) save(subject)} to ensure the constructed 323 * {@code Subject}'s state is accessible for future requests/invocations if necessary.</li> 324 * <li>returns the constructed {@code Subject} instance.</li> 325 * </ol> 326 * 327 * @param subjectContext any data needed to direct how the Subject should be constructed. 328 * @return the {@code Subject} instance reflecting the specified contextual data. 329 * @see #ensureSecurityManager(org.apache.shiro.subject.SubjectContext) 330 * @see #resolveSession(org.apache.shiro.subject.SubjectContext) 331 * @see #resolvePrincipals(org.apache.shiro.subject.SubjectContext) 332 * @see #doCreateSubject(org.apache.shiro.subject.SubjectContext) 333 * @see #save(org.apache.shiro.subject.Subject) 334 * @since 1.0 335 */ 336 public Subject createSubject(SubjectContext subjectContext) { 337 //create a copy so we don't modify the argument's backing map: 338 SubjectContext context = copy(subjectContext); 339 340 //ensure that the context has a SecurityManager instance, and if not, add one: 341 context = ensureSecurityManager(context); 342 343 //Resolve an associated Session (usually based on a referenced session ID), and place it in the context before 344 //sending to the SubjectFactory. The SubjectFactory should not need to know how to acquire sessions as the 345 //process is often environment specific - better to shield the SF from these details: 346 context = resolveSession(context); 347 348 //Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first 349 //if possible before handing off to the SubjectFactory: 350 context = resolvePrincipals(context); 351 352 Subject subject = doCreateSubject(context); 353 354 //save this subject for future reference if necessary: 355 //(this is needed here in case rememberMe principals were resolved and they need to be stored in the 356 //session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation). 357 //Added in 1.2: 358 if (context.isSessionCreationEnabled()) { 359 save(subject); 360 } 361 362 return subject; 363 } 364 365 /** 366 * Actually creates a {@code Subject} instance by delegating to the internal 367 * {@link #getSubjectFactory() subjectFactory}. By the time this method is invoked, all possible 368 * {@code SubjectContext} data (session, principals, et al.) has been made accessible using all known heuristics 369 * and will be accessible to the {@code subjectFactory} via the {@code subjectContext.resolve*} methods. 370 * 371 * @param context the populated context (data map) to be used by the {@code SubjectFactory} when creating a 372 * {@code Subject} instance. 373 * @return a {@code Subject} instance reflecting the data in the specified {@code SubjectContext} data map. 374 * @see #getSubjectFactory() 375 * @see SubjectFactory#createSubject(org.apache.shiro.subject.SubjectContext) 376 * @since 1.2 377 */ 378 protected Subject doCreateSubject(SubjectContext context) { 379 return getSubjectFactory().createSubject(context); 380 } 381 382 /** 383 * Saves the subject's state to a persistent location for future reference if necessary. 384 * <p/> 385 * This implementation merely delegates to the internal {@link #setSubjectDAO(SubjectDAO) subjectDAO} and calls 386 * {@link SubjectDAO#save(org.apache.shiro.subject.Subject) subjectDAO.save(subject)}. 387 * 388 * @param subject the subject for which state will potentially be persisted 389 * @see SubjectDAO#save(org.apache.shiro.subject.Subject) 390 * @since 1.2 391 */ 392 protected void save(Subject subject) { 393 this.subjectDAO.save(subject); 394 } 395 396 /** 397 * Removes (or 'unbinds') the Subject's state from the application, typically called during {@link #logout}.. 398 * <p/> 399 * This implementation merely delegates to the internal {@link #setSubjectDAO(SubjectDAO) subjectDAO} and calls 400 * {@link SubjectDAO#delete(org.apache.shiro.subject.Subject) delete(subject)}. 401 * 402 * @param subject the subject for which state will be removed 403 * @see SubjectDAO#delete(org.apache.shiro.subject.Subject) 404 * @since 1.2 405 */ 406 protected void delete(Subject subject) { 407 this.subjectDAO.delete(subject); 408 } 409 410 /** 411 * Determines if there is a {@code SecurityManager} instance in the context, and if not, adds 'this' to the 412 * context. This ensures the SubjectFactory instance will have access to a SecurityManager during Subject 413 * construction if necessary. 414 * 415 * @param context the subject context data that may contain a SecurityManager instance. 416 * @return The SubjectContext to use to pass to a {@link SubjectFactory} for subject creation. 417 * @since 1.0 418 */ 419 @SuppressWarnings({"unchecked"}) 420 protected SubjectContext ensureSecurityManager(SubjectContext context) { 421 if (context.resolveSecurityManager() != null) { 422 LOGGER.trace("Context already contains a SecurityManager instance. Returning."); 423 return context; 424 } 425 LOGGER.trace("No SecurityManager found in context. Adding self reference."); 426 context.setSecurityManager(this); 427 return context; 428 } 429 430 /** 431 * Attempts to resolve any associated session based on the context and returns a 432 * context that represents this resolved {@code Session} to ensure it may be referenced if necessary by the 433 * invoked {@link SubjectFactory} that performs actual {@link Subject} construction. 434 * <p/> 435 * If there is a {@code Session} already in the context because that is what the caller wants to be used for 436 * {@code Subject} construction, or if no session is resolved, this method effectively does nothing 437 * returns the context method argument unaltered. 438 * 439 * @param context the subject context data that may resolve a Session instance. 440 * @return The context to use to pass to a {@link SubjectFactory} for subject creation. 441 * @since 1.0 442 */ 443 @SuppressWarnings({"unchecked"}) 444 protected SubjectContext resolveSession(SubjectContext context) { 445 if (context.resolveSession() != null) { 446 LOGGER.debug("Context already contains a session. Returning."); 447 return context; 448 } 449 try { 450 //Context couldn't resolve it directly, let's see if we can since we have direct access to 451 //the session manager: 452 Session session = resolveContextSession(context); 453 if (session != null) { 454 context.setSession(session); 455 } 456 } catch (InvalidSessionException e) { 457 LOGGER.debug("Resolved SubjectContext context session is invalid. Ignoring and creating an anonymous " 458 + "(session-less) Subject instance.", e); 459 } 460 return context; 461 } 462 463 protected Session resolveContextSession(SubjectContext context) throws InvalidSessionException { 464 SessionKey key = getSessionKey(context); 465 if (key != null) { 466 return getSession(key); 467 } 468 return null; 469 } 470 471 protected SessionKey getSessionKey(SubjectContext context) { 472 Serializable sessionId = context.getSessionId(); 473 if (sessionId != null) { 474 return new DefaultSessionKey(sessionId); 475 } 476 return null; 477 } 478 479 private static boolean isEmpty(PrincipalCollection pc) { 480 return pc == null || pc.isEmpty(); 481 } 482 483 /** 484 * Attempts to resolve an identity (a {@link PrincipalCollection}) for the context using heuristics. This 485 * implementation functions as follows: 486 * <ol> 487 * <li>Check the context to see if it can already {@link SubjectContext#resolvePrincipals resolve an identity}. If 488 * so, this method does nothing and returns the method argument unaltered.</li> 489 * <li>Check for a RememberMe identity by calling {@link #getRememberedIdentity}. If that method returns a 490 * non-null value, place the remembered {@link PrincipalCollection} in the context.</li> 491 * </ol> 492 * 493 * @param context the subject context data that may provide (directly or indirectly through one of its values) a 494 * {@link PrincipalCollection} identity. 495 * @return The Subject context to use to pass to a {@link SubjectFactory} for subject creation. 496 * @since 1.0 497 */ 498 @SuppressWarnings({"unchecked"}) 499 protected SubjectContext resolvePrincipals(SubjectContext context) { 500 501 PrincipalCollection principals = context.resolvePrincipals(); 502 503 if (isEmpty(principals)) { 504 LOGGER.trace("No identity (PrincipalCollection) found in the context. Looking for a remembered identity."); 505 506 principals = getRememberedIdentity(context); 507 508 if (!isEmpty(principals)) { 509 LOGGER.debug("Found remembered PrincipalCollection. Adding to the context to be used " 510 + "for subject construction by the SubjectFactory."); 511 512 context.setPrincipals(principals); 513 514 // The following call was removed (commented out) in Shiro 1.2 because it uses the session as an 515 // implementation strategy. Session use for Shiro's own needs should be controlled in a single place 516 // to be more manageable for end-users: there are a number of stateless (e.g. REST) applications that 517 // use Shiro that need to ensure that sessions are only used when desirable. If Shiro's internal 518 // implementations used Subject sessions (setting attributes) whenever we wanted, it would be much 519 // harder for end-users to control when/where that occurs. 520 // 521 // Because of this, the SubjectDAO was created as the single point of control, and session state logic 522 // has been moved to the DefaultSubjectDAO implementation. 523 524 // Removed in Shiro 1.2. SHIRO-157 is still satisfied by the new DefaultSubjectDAO implementation 525 // introduced in 1.2 526 // Satisfies SHIRO-157: 527 // bindPrincipalsToSession(principals, context); 528 529 } else { 530 LOGGER.trace("No remembered identity found. Returning original context."); 531 } 532 } 533 534 return context; 535 } 536 537 protected SessionContext createSessionContext(SubjectContext subjectContext) { 538 DefaultSessionContext sessionContext = new DefaultSessionContext(); 539 if (!CollectionUtils.isEmpty(subjectContext)) { 540 sessionContext.putAll(subjectContext); 541 } 542 Serializable sessionId = subjectContext.getSessionId(); 543 if (sessionId != null) { 544 sessionContext.setSessionId(sessionId); 545 } 546 String host = subjectContext.resolveHost(); 547 if (host != null) { 548 sessionContext.setHost(host); 549 } 550 return sessionContext; 551 } 552 553 public void logout(Subject subject) { 554 555 if (subject == null) { 556 throw new IllegalArgumentException("Subject method argument cannot be null."); 557 } 558 559 beforeLogout(subject); 560 561 PrincipalCollection principals = subject.getPrincipals(); 562 if (principals != null && !principals.isEmpty()) { 563 if (LOGGER.isDebugEnabled()) { 564 LOGGER.debug("Logging out subject with primary principal {}", principals.getPrimaryPrincipal()); 565 } 566 Authenticator authc = getAuthenticator(); 567 if (authc instanceof LogoutAware) { 568 ((LogoutAware) authc).onLogout(principals); 569 } 570 } 571 572 try { 573 delete(subject); 574 } catch (Exception e) { 575 if (LOGGER.isDebugEnabled()) { 576 String msg = "Unable to cleanly unbind Subject. Ignoring (logging out)."; 577 LOGGER.debug(msg, e); 578 } 579 } finally { 580 try { 581 stopSession(subject); 582 } catch (Exception e) { 583 if (LOGGER.isDebugEnabled()) { 584 String msg = "Unable to cleanly stop Session for Subject [" + subject.getPrincipal() + "] " 585 + "Ignoring (logging out)."; 586 LOGGER.debug(msg, e); 587 } 588 } 589 } 590 } 591 592 protected void stopSession(Subject subject) { 593 Session s = subject.getSession(false); 594 if (s != null) { 595 s.stop(); 596 } 597 } 598 599 /** 600 * Unbinds or removes the Subject's state from the application, typically called during {@link #logout}. 601 * <p/> 602 * This has been deprecated in Shiro 1.2 in favor of the {@link #delete(org.apache.shiro.subject.Subject) delete} 603 * method. The implementation has been updated to invoke that method. 604 * 605 * @param subject the subject to unbind from the application as it will no longer be used. 606 * @deprecated in Shiro 1.2 in favor of {@link #delete(org.apache.shiro.subject.Subject)} 607 */ 608 @Deprecated 609 @SuppressWarnings({"UnusedDeclaration"}) 610 protected void unbind(Subject subject) { 611 delete(subject); 612 } 613 614 protected PrincipalCollection getRememberedIdentity(SubjectContext subjectContext) { 615 RememberMeManager rmm = getRememberMeManager(); 616 if (rmm != null) { 617 try { 618 return rmm.getRememberedPrincipals(subjectContext); 619 } catch (Exception e) { 620 if (LOGGER.isWarnEnabled()) { 621 String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() 622 + "] threw an exception during getRememberedPrincipals()."; 623 LOGGER.warn(msg, e); 624 } 625 } 626 } 627 return null; 628 } 629}