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 }