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 */
019 package org.apache.shiro.subject.support;
020
021 import org.apache.shiro.mgt.SecurityManager;
022 import org.apache.shiro.subject.Subject;
023 import org.apache.shiro.util.CollectionUtils;
024 import org.apache.shiro.util.ThreadContext;
025 import org.apache.shiro.util.ThreadState;
026
027 import java.util.Map;
028
029 /**
030 * Manages thread-state for {@link Subject Subject} access (supporting
031 * {@code SecurityUtils.}{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()} calls)
032 * during a thread's execution.
033 * <p/>
034 * The {@link #bind bind} method will bind a {@link Subject} and a
035 * {@link org.apache.shiro.mgt.SecurityManager SecurityManager} to the {@link ThreadContext} so they can be retrieved
036 * from the {@code ThreadContext} later by any
037 * {@code SecurityUtils.}{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()} calls that might occur during
038 * the thread's execution.
039 *
040 * @since 1.0
041 * @author Les Hazlewood
042 */
043 public class SubjectThreadState implements ThreadState {
044
045 private Map<Object, Object> originalResources;
046
047 private final Subject subject;
048 private transient SecurityManager securityManager;
049
050 /**
051 * Creates a new {@code SubjectThreadState} that will bind and unbind the specified {@code Subject} to the
052 * thread
053 *
054 * @param subject the {@code Subject} instance to bind and unbind from the {@link ThreadContext}.
055 */
056 public SubjectThreadState(Subject subject) {
057 if (subject == null) {
058 throw new IllegalArgumentException("Subject argument cannot be null.");
059 }
060 this.subject = subject;
061
062 SecurityManager securityManager = null;
063 if ( subject instanceof DelegatingSubject) {
064 securityManager = ((DelegatingSubject)subject).getSecurityManager();
065 }
066 if ( securityManager == null) {
067 securityManager = ThreadContext.getSecurityManager();
068 }
069 this.securityManager = securityManager;
070 }
071
072 /**
073 * Returns the {@code Subject} instance managed by this {@code ThreadState} implementation.
074 *
075 * @return the {@code Subject} instance managed by this {@code ThreadState} implementation.
076 */
077 protected Subject getSubject() {
078 return this.subject;
079 }
080
081 /**
082 * Binds a {@link Subject} and {@link org.apache.shiro.mgt.SecurityManager SecurityManager} to the
083 * {@link ThreadContext} so they can be retrieved later by any
084 * {@code SecurityUtils.}{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()} calls that might occur
085 * during the thread's execution.
086 * <p/>
087 * Prior to binding, the {@code ThreadContext}'s existing {@link ThreadContext#getResources() resources} are
088 * retained so they can be restored later via the {@link #restore restore} call.
089 */
090 public void bind() {
091 SecurityManager securityManager = this.securityManager;
092 if ( securityManager == null ) {
093 //try just in case the constructor didn't find one at the time:
094 securityManager = ThreadContext.getSecurityManager();
095 }
096 this.originalResources = ThreadContext.getResources();
097 ThreadContext.remove();
098
099 ThreadContext.bind(this.subject);
100 if (securityManager != null) {
101 ThreadContext.bind(securityManager);
102 }
103 }
104
105 /**
106 * {@link ThreadContext#remove Remove}s all thread-state that was bound by this instance. If any previous
107 * thread-bound resources existed prior to the {@link #bind bind} call, they are restored back to the
108 * {@code ThreadContext} to ensure the thread state is exactly as it was before binding.
109 */
110 public void restore() {
111 ThreadContext.remove();
112 if (!CollectionUtils.isEmpty(this.originalResources)) {
113 ThreadContext.setResources(this.originalResources);
114 }
115 }
116
117 /**
118 * Completely {@link ThreadContext#remove removes} the {@code ThreadContext} state. Typically this method should
119 * only be called in special cases - it is more 'correct' to {@link #restore restore} a thread to its previous
120 * state than to clear it entirely.
121 */
122 public void clear() {
123 ThreadContext.remove();
124 }
125 }