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.subject.support;
20  
21  import org.apache.shiro.mgt.SecurityManager;
22  import org.apache.shiro.subject.Subject;
23  import org.apache.shiro.util.CollectionUtils;
24  import org.apache.shiro.util.ThreadContext;
25  import org.apache.shiro.util.ThreadState;
26  
27  import java.util.Map;
28  
29  /**
30   * Manages thread-state for {@link Subject Subject} access (supporting
31   * {@code SecurityUtils.}{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()} calls)
32   * during a thread's execution.
33   * <p/>
34   * The {@link #bind bind} method will bind a {@link Subject} and a
35   * {@link org.apache.shiro.mgt.SecurityManager SecurityManager} to the {@link ThreadContext} so they can be retrieved
36   * from the {@code ThreadContext} later by any
37   * {@code SecurityUtils.}{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()} calls that might occur during
38   * the thread's execution.
39   *
40   * @since 1.0
41   */
42  public class SubjectThreadState implements ThreadState {
43  
44      private Map<Object, Object> originalResources;
45  
46      private final Subject subject;
47      private transient SecurityManager securityManager;
48  
49      /**
50       * Creates a new {@code SubjectThreadState} that will bind and unbind the specified {@code Subject} to the
51       * thread
52       *
53       * @param subject the {@code Subject} instance to bind and unbind from the {@link ThreadContext}.
54       */
55      public SubjectThreadState(Subject subject) {
56          if (subject == null) {
57              throw new IllegalArgumentException("Subject argument cannot be null.");
58          }
59          this.subject = subject;
60  
61          SecurityManager securityManager = null;
62          if ( subject instanceof DelegatingSubject) {
63              securityManager = ((DelegatingSubject)subject).getSecurityManager();
64          }
65          if ( securityManager == null) {
66              securityManager = ThreadContext.getSecurityManager();
67          }
68          this.securityManager = securityManager;
69      }
70  
71      /**
72       * Returns the {@code Subject} instance managed by this {@code ThreadState} implementation.
73       *
74       * @return the {@code Subject} instance managed by this {@code ThreadState} implementation.
75       */
76      protected Subject getSubject() {
77          return this.subject;
78      }
79  
80      /**
81       * Binds a {@link Subject} and {@link org.apache.shiro.mgt.SecurityManager SecurityManager} to the
82       * {@link ThreadContext} so they can be retrieved later by any
83       * {@code SecurityUtils.}{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()} calls that might occur
84       * during the thread's execution.
85       * <p/>
86       * Prior to binding, the {@code ThreadContext}'s existing {@link ThreadContext#getResources() resources} are
87       * retained so they can be restored later via the {@link #restore restore} call.
88       */
89      public void bind() {
90          SecurityManager securityManager = this.securityManager;
91          if ( securityManager == null ) {
92              //try just in case the constructor didn't find one at the time:
93              securityManager = ThreadContext.getSecurityManager();
94          }
95          this.originalResources = ThreadContext.getResources();
96          ThreadContext.remove();
97  
98          ThreadContext.bind(this.subject);
99          if (securityManager != null) {
100             ThreadContext.bind(securityManager);
101         }
102     }
103 
104     /**
105      * {@link ThreadContext#remove Remove}s all thread-state that was bound by this instance.  If any previous
106      * thread-bound resources existed prior to the {@link #bind bind} call, they are restored back to the
107      * {@code ThreadContext} to ensure the thread state is exactly as it was before binding.
108      */
109     public void restore() {
110         ThreadContext.remove();
111         if (!CollectionUtils.isEmpty(this.originalResources)) {
112             ThreadContext.setResources(this.originalResources);
113         }
114     }
115 
116     /**
117      * Completely {@link ThreadContext#remove removes} the {@code ThreadContext} state.  Typically this method should
118      * only be called in special cases - it is more 'correct' to {@link #restore restore} a thread to its previous
119      * state than to clear it entirely.
120      */
121     public void clear() {
122         ThreadContext.remove();
123     }
124 }