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.spring.remoting;
20  
21  import org.apache.shiro.SecurityUtils;
22  import org.apache.shiro.mgt.SecurityManager;
23  import org.apache.shiro.subject.ExecutionException;
24  import org.apache.shiro.subject.Subject;
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  import org.springframework.remoting.support.DefaultRemoteInvocationExecutor;
28  import org.springframework.remoting.support.RemoteInvocation;
29  
30  import java.io.Serializable;
31  import java.lang.reflect.InvocationTargetException;
32  import java.util.concurrent.Callable;
33  
34  
35  /**
36   * An implementation of the Spring {@link org.springframework.remoting.support.RemoteInvocationExecutor}
37   * that binds a {@code sessionId} to the incoming thread to make it available to the {@code SecurityManager}
38   * implementation during the thread execution.  The {@code SecurityManager} implementation can use this sessionId
39   * to reconstitute the {@code Subject} instance based on persistent state in the corresponding {@code Session}.
40   *
41   * @since 0.1
42   */
43  public class SecureRemoteInvocationExecutor extends DefaultRemoteInvocationExecutor {
44  
45      //TODO - complete JavaDoc
46  
47      /*--------------------------------------------
48      |             C O N S T A N T S             |
49      ============================================*/
50  
51      /*--------------------------------------------
52      |    I N S T A N C E   V A R I A B L E S    |
53      ============================================*/
54      private static final Logger LOGGER = LoggerFactory.getLogger(SecureRemoteInvocationExecutor.class);
55  
56      /**
57       * The SecurityManager used to retrieve realms that should be associated with the
58       * created <tt>Subject</tt>s upon remote invocation.
59       */
60      private SecurityManager securityManager;
61  
62      /*--------------------------------------------
63      |         C O N S T R U C T O R S           |
64      ============================================*/
65  
66      /*--------------------------------------------
67      |  A C C E S S O R S / M O D I F I E R S    |
68      ============================================*/
69  
70      public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) {
71          this.securityManager = securityManager;
72      }
73  
74      /*--------------------------------------------
75      |               M E T H O D S               |
76      ============================================*/
77      @SuppressWarnings({"unchecked"})
78      public Object invoke(final RemoteInvocation invocation, final Object targetObject)
79              throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
80  
81          try {
82              SecurityManager securityManager =
83                      this.securityManager != null ? this.securityManager : SecurityUtils.getSecurityManager();
84  
85              Subject.Builder builder = new Subject.Builder(securityManager);
86  
87              String host = (String) invocation.getAttribute(SecureRemoteInvocationFactory.HOST_KEY);
88              if (host != null) {
89                  builder.host(host);
90              }
91  
92              Serializable sessionId = invocation.getAttribute(SecureRemoteInvocationFactory.SESSION_ID_KEY);
93              if (sessionId != null) {
94                  builder.sessionId(sessionId);
95              } else {
96                  if (LOGGER.isTraceEnabled()) {
97                      LOGGER.trace("RemoteInvocation did not contain a Shiro Session id attribute under "
98                              + "key [" + SecureRemoteInvocationFactory.SESSION_ID_KEY + "].  A Subject based "
99                              + "on an existing Session will not be available during the method invocation.");
100                 }
101             }
102 
103             Subject subject = builder.buildSubject();
104             return subject.execute(new Callable() {
105                 public Object call() throws Exception {
106                     return SecureRemoteInvocationExecutor.super.invoke(invocation, targetObject);
107                 }
108             });
109         } catch (ExecutionException e) {
110             Throwable cause = e.getCause();
111             if (cause instanceof NoSuchMethodException) {
112                 throw (NoSuchMethodException) cause;
113             } else if (cause instanceof IllegalAccessException) {
114                 throw (IllegalAccessException) cause;
115             } else if (cause instanceof InvocationTargetException) {
116                 throw (InvocationTargetException) cause;
117             } else {
118                 throw new InvocationTargetException(cause);
119             }
120         } catch (Throwable t) {
121             throw new InvocationTargetException(t);
122         }
123     }
124 }