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 */ 019package org.apache.shiro.spring.remoting; 020 021import org.apache.shiro.SecurityUtils; 022import org.apache.shiro.mgt.SecurityManager; 023import org.apache.shiro.subject.ExecutionException; 024import org.apache.shiro.subject.Subject; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027import org.springframework.remoting.support.DefaultRemoteInvocationExecutor; 028import org.springframework.remoting.support.RemoteInvocation; 029 030import java.io.Serializable; 031import java.lang.reflect.InvocationTargetException; 032import java.util.concurrent.Callable; 033 034 035/** 036 * An implementation of the Spring {@link org.springframework.remoting.support.RemoteInvocationExecutor} 037 * that binds a {@code sessionId} to the incoming thread to make it available to the {@code SecurityManager} 038 * implementation during the thread execution. The {@code SecurityManager} implementation can use this sessionId 039 * to reconstitute the {@code Subject} instance based on persistent state in the corresponding {@code Session}. 040 * 041 * @since 0.1 042 */ 043public class SecureRemoteInvocationExecutor extends DefaultRemoteInvocationExecutor { 044 045 //TODO - complete JavaDoc 046 047 /*-------------------------------------------- 048 | C O N S T A N T S | 049 ============================================*/ 050 051 /*-------------------------------------------- 052 | I N S T A N C E V A R I A B L E S | 053 ============================================*/ 054 private static final Logger LOGGER = LoggerFactory.getLogger(SecureRemoteInvocationExecutor.class); 055 056 /** 057 * The SecurityManager used to retrieve realms that should be associated with the 058 * created <tt>Subject</tt>s upon remote invocation. 059 */ 060 private SecurityManager securityManager; 061 062 /*-------------------------------------------- 063 | C O N S T R U C T O R S | 064 ============================================*/ 065 066 /*-------------------------------------------- 067 | A C C E S S O R S / M O D I F I E R S | 068 ============================================*/ 069 070 public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) { 071 this.securityManager = securityManager; 072 } 073 074 /*-------------------------------------------- 075 | M E T H O D S | 076 ============================================*/ 077 @SuppressWarnings({"unchecked"}) 078 public Object invoke(final RemoteInvocation invocation, final Object targetObject) 079 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 080 081 try { 082 SecurityManager securityManager = 083 this.securityManager != null ? this.securityManager : SecurityUtils.getSecurityManager(); 084 085 Subject.Builder builder = new Subject.Builder(securityManager); 086 087 String host = (String) invocation.getAttribute(SecureRemoteInvocationFactory.HOST_KEY); 088 if (host != null) { 089 builder.host(host); 090 } 091 092 Serializable sessionId = invocation.getAttribute(SecureRemoteInvocationFactory.SESSION_ID_KEY); 093 if (sessionId != null) { 094 builder.sessionId(sessionId); 095 } else { 096 if (LOGGER.isTraceEnabled()) { 097 LOGGER.trace("RemoteInvocation did not contain a Shiro Session id attribute under " 098 + "key [" + SecureRemoteInvocationFactory.SESSION_ID_KEY + "]. A Subject based " 099 + "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}