0
# RMI Context Propagation
1
2
Security context propagation for RMI remoting, enabling transparent transmission of authentication information from client to server in distributed RMI applications. Maintains Spring Security context across JVM boundaries.
3
4
**Deprecation Notice**: RMI context propagation APIs are deprecated as of Spring Security 5.6.0 with no replacement.
5
6
## Capabilities
7
8
### Context Propagating Remote Invocation
9
10
Custom RemoteInvocation implementation that captures the security context on the client side and restores it on the server side during RMI method invocation.
11
12
```java { .api }
13
public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
14
/**
15
* Constructs the object, storing the principal and credentials extracted
16
* from the client-side security context.
17
* @param methodInvocation the method to invoke
18
*/
19
public ContextPropagatingRemoteInvocation(MethodInvocation methodInvocation);
20
21
/**
22
* Invoked on the server-side. Creates an unauthenticated Authentication
23
* instance from transmitted principal and credentials for processing by
24
* the AuthenticationManager.
25
* @param targetObject the target object to apply the invocation to
26
* @return the invocation result
27
* @throws NoSuchMethodException if the method name could not be resolved
28
* @throws IllegalAccessException if the method could not be accessed
29
* @throws InvocationTargetException if the method invocation resulted in an exception
30
*/
31
public Object invoke(Object targetObject)
32
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException;
33
34
/**
35
* Creates the server-side authentication request object.
36
* Default implementation creates UsernamePasswordAuthenticationToken with unauthenticated state.
37
* Override to create different token types.
38
* @param principal the transmitted principal
39
* @param credentials the transmitted credentials
40
* @return Authentication instance for server-side processing
41
*/
42
protected Authentication createAuthenticationRequest(String principal, String credentials);
43
}
44
```
45
46
### Context Propagating Remote Invocation Factory
47
48
Factory for creating ContextPropagatingRemoteInvocation instances, used by RMI proxy factory beans to enable security context propagation.
49
50
```java { .api }
51
public class ContextPropagatingRemoteInvocationFactory implements RemoteInvocationFactory {
52
/**
53
* Creates a remote invocation that propagates the current security context.
54
* @param methodInvocation the method invocation to wrap
55
* @return RemoteInvocation with security context propagation
56
*/
57
public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation);
58
}
59
```
60
61
## Usage Examples
62
63
### Basic RMI Client Configuration
64
65
```java
66
import org.springframework.remoting.rmi.RmiProxyFactoryBean;
67
import org.springframework.security.remoting.rmi.ContextPropagatingRemoteInvocationFactory;
68
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
69
70
// Define your service interface
71
public interface MyRemoteService {
72
String processSecureRequest(String input);
73
List<String> getUserData(String userId);
74
}
75
76
// Configure RMI proxy with security context propagation
77
RmiProxyFactoryBean proxy = new RmiProxyFactoryBean();
78
proxy.setServiceUrl("rmi://localhost:1099/MyRemoteService");
79
proxy.setServiceInterface(MyRemoteService.class);
80
proxy.setRefreshStubOnConnectFailure(true);
81
82
// Add security context propagation
83
ContextPropagatingRemoteInvocationFactory factory =
84
new ContextPropagatingRemoteInvocationFactory();
85
proxy.setRemoteInvocationFactory(factory);
86
87
// Get the proxy instance
88
MyRemoteService service = (MyRemoteService) proxy.getObject();
89
90
// Use the service - security context will be propagated automatically
91
String result = service.processSecureRequest("sensitive data");
92
```
93
94
### Spring Bean Configuration
95
96
```xml
97
<!-- RMI Proxy with Security Context Propagation -->
98
<bean id="myRemoteService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
99
<property name="serviceUrl" value="rmi://localhost:1099/MyRemoteService"/>
100
<property name="serviceInterface" value="com.example.MyRemoteService"/>
101
<property name="refreshStubOnConnectFailure" value="true"/>
102
<property name="remoteInvocationFactory" ref="contextPropagatingRemoteInvocationFactory"/>
103
</bean>
104
105
<!-- Remote Invocation Factory for Security Context Propagation -->
106
<bean id="contextPropagatingRemoteInvocationFactory"
107
class="org.springframework.security.remoting.rmi.ContextPropagatingRemoteInvocationFactory"/>
108
```
109
110
### Client-Side Usage with Security Context
111
112
```java
113
import org.springframework.security.core.Authentication;
114
import org.springframework.security.core.context.SecurityContextHolder;
115
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
116
117
// Set up authentication on the client side
118
Authentication auth = new UsernamePasswordAuthenticationToken("user", "password");
119
SecurityContextHolder.getContext().setAuthentication(auth);
120
121
try {
122
// Remote call will propagate the security context to the server
123
MyRemoteService service = getMyRemoteService(); // configured with ContextPropagatingRemoteInvocationFactory
124
String result = service.processSecureRequest("confidential data");
125
System.out.println("Result: " + result);
126
} finally {
127
// Clean up client security context
128
SecurityContextHolder.clearContext();
129
}
130
```
131
132
### Server-Side RMI Service Configuration
133
134
```java
135
import org.springframework.remoting.rmi.RmiServiceExporter;
136
137
// Server-side service implementation
138
@Component
139
public class MyRemoteServiceImpl implements MyRemoteService {
140
141
@Override
142
public String processSecureRequest(String input) {
143
// Access the propagated security context
144
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
145
if (auth != null && auth.isAuthenticated()) {
146
String username = auth.getName();
147
// Process request with security context
148
return "Processed by " + username + ": " + input;
149
}
150
throw new SecurityException("No authenticated user");
151
}
152
}
153
154
// RMI service exporter configuration
155
@Bean
156
public RmiServiceExporter rmiServiceExporter(MyRemoteService myRemoteService) {
157
RmiServiceExporter exporter = new RmiServiceExporter();
158
exporter.setServiceName("MyRemoteService");
159
exporter.setService(myRemoteService);
160
exporter.setServiceInterface(MyRemoteService.class);
161
exporter.setRegistryPort(1099);
162
return exporter;
163
}
164
```
165
166
### Custom Authentication Token Creation
167
168
```java
169
import org.springframework.security.remoting.rmi.ContextPropagatingRemoteInvocation;
170
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
171
import org.springframework.security.core.Authentication;
172
import org.springframework.security.core.authority.SimpleGrantedAuthority;
173
import java.util.Arrays;
174
175
public class CustomContextPropagatingRemoteInvocation extends ContextPropagatingRemoteInvocation {
176
177
public CustomContextPropagatingRemoteInvocation(MethodInvocation methodInvocation) {
178
super(methodInvocation);
179
}
180
181
@Override
182
protected Authentication createAuthenticationRequest(String principal, String credentials) {
183
// Create unauthenticated token (default implementation)
184
return UsernamePasswordAuthenticationToken.unauthenticated(principal, credentials);
185
}
186
}
187
188
// Custom factory to use the custom invocation
189
public class CustomRemoteInvocationFactory implements RemoteInvocationFactory {
190
@Override
191
public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
192
return new CustomContextPropagatingRemoteInvocation(methodInvocation);
193
}
194
}
195
```
196
197
## Security Context Propagation Process
198
199
### Client-Side Behavior
200
201
1. **Context Capture**: When a remote method is called, ContextPropagatingRemoteInvocation extracts:
202
- Principal name from current Authentication
203
- Credentials as string from current Authentication
204
2. **Serialization**: Principal and credentials are serialized as strings for transmission
205
3. **Transmission**: The RemoteInvocation with security data is sent to the server
206
207
### Server-Side Behavior
208
209
1. **Deserialization**: Server receives the ContextPropagatingRemoteInvocation
210
2. **Authentication Creation**: Creates unauthenticated UsernamePasswordAuthenticationToken from transmitted data
211
3. **Context Setup**: Sets up SecurityContext with the Authentication before method execution
212
4. **Method Execution**: Target method executes with security context available
213
5. **Context Cleanup**: SecurityContext is cleared after method execution completes
214
215
### Security Considerations
216
217
- **Credentials Transmission**: Credentials are transmitted as plaintext strings - ensure secure transport (SSL/TLS)
218
- **Authentication State**: Transmitted authentication is marked as unauthenticated, requiring server-side authentication
219
- **Token Type**: Default implementation creates UsernamePasswordAuthenticationToken - override for custom token types
220
- **Serialization Safety**: Implementation interprets values as strings to avoid serialization-based attacks
221
222
## Integration with Spring Security
223
224
### Server-Side Authentication
225
226
The propagated authentication typically requires server-side processing:
227
228
```java
229
@Configuration
230
@EnableGlobalMethodSecurity(prePostEnabled = true)
231
public class RmiSecurityConfig {
232
233
@Bean
234
public AuthenticationManager authenticationManager() {
235
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
236
provider.setUserDetailsService(userDetailsService());
237
provider.setPasswordEncoder(passwordEncoder());
238
return new ProviderManager(provider);
239
}
240
241
// Configure authentication for RMI services
242
@Bean
243
public MethodSecurityInterceptor methodSecurityInterceptor() {
244
MethodSecurityInterceptor interceptor = new MethodSecurityInterceptor();
245
interceptor.setAuthenticationManager(authenticationManager());
246
// Configure access decision manager
247
return interceptor;
248
}
249
}
250
```
251
252
### Method-Level Security
253
254
```java
255
@Component
256
public class SecureRemoteServiceImpl implements SecureRemoteService {
257
258
@PreAuthorize("hasRole('USER')")
259
public String processUserRequest(String request) {
260
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
261
return "Processed by " + auth.getName() + ": " + request;
262
}
263
264
@PreAuthorize("hasRole('ADMIN')")
265
public void performAdminOperation() {
266
// Admin-only operation
267
}
268
}