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