【发布时间】:2016-07-19 10:46:30
【问题描述】:
我正在尝试将两个简单的 Spring Web 应用程序与 Spring Integration 集成,并且我想在两个应用程序之间传播 Spring SecurityContext,但我还没有找到可行的解决方案。
这两个应用程序非常相似,只有一些配置不同:其中一个必须是“调用者”,另一个必须是“接收者”。
为了获得预期的结果,我正在使用 Spring Security,并且我已经使用以下 Spring Security 配置配置了两个应用程序:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.1.xsd">
<http auto-config="true">
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/service/**" access="permitAll" />
<intercept-url pattern="/home" access="permitAll" />
<intercept-url pattern="/admin**" access="hasRole('ADMIN')" />
<intercept-url pattern="/dba**" access="hasRole('ADMIN') and hasRole('DBA')" />
<form-login authentication-failure-url="/accessDenied" />
</http>
<authentication-manager id="authenticationManager">
<authentication-provider>
<user-service>
<user name="user" password="user" authorities="ROLE_USER" />
<user name="admin" password="admin" authorities="ROLE_ADMIN" />
<user name="dba" password="dba" authorities="ROLE_ADMIN,ROLE_DBA" />
</user-service>
</authentication-provider>
</authentication-manager>
<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<beans:constructor-arg>
<beans:list>
<beans:bean class="org.springframework.security.access.vote.RoleVoter" />
<beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</beans:list>
</beans:constructor-arg>
</beans:bean>
</beans:beans>
这是有效的:如果我转到 url http://localhost:8080/caller/login,登录过程由 Spring Security 层正确管理。
所以我正在尝试配置 Spring Integration 模块以“集成”两个应用程序;我的想法(错误?)是使用来自“调用者”的http:outbound-gateway,它在特定的 URL 调用“接收者”。
另一方面,有一个http:inbound-gateway 管理请求。
这里是“调用者”端的配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:int-security="http://www.springframework.org/schema/integration/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-4.3.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http-4.3.xsd
http://www.springframework.org/schema/integration/security http://www.springframework.org/schema/integration/security/spring-integration-security-4.2.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd">
<int:channel id="requestChannel">
<int:dispatcher task-executor="executor"/>
</int:channel>
<int:channel-interceptor ref="requestChannelInterceptor" pattern="requestChannel">
</int:channel-interceptor>
<task:executor id="executor" pool-size="5"/>
<bean id="requestChannelInterceptor" class="org.springframework.integration.security.channel.SecurityContextPropagationChannelInterceptor"></bean>
<bean id="requestChannelBean" class="test.spring.webapp.client.MessagingChannel">
<property name="requestChannel" ref="requestChannel"></property>
</bean>
<int-http:outbound-gateway id="gateway" request-channel="requestChannel"
encode-uri="true" url="http://localhost:8080/receiver/service/{request}"
http-method="GET" >
<int-http:uri-variable name="request" expression="payload"/>
</int-http:outbound-gateway>
<int-security:secured-channels access-decision-manager="accessDecisionManager">
<int-security:access-policy pattern="requestChannel" send-access="ROLE_ADMIN,ROLE_USER,ROLE_DBA,ROLE_ANONYMOUS"/>
</int-security:secured-channels>
</beans>
这里是“接收器”端的配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:int-security="http://www.springframework.org/schema/integration/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-4.3.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http-4.3.xsd
http://www.springframework.org/schema/integration/security http://www.springframework.org/schema/integration/security/spring-integration-security-4.2.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd">
<int:channel id="requestChannel">
<int:dispatcher task-executor="executor"/>
</int:channel>
<int:channel-interceptor ref="requestChannelInterceptor" pattern="requestChannel">
</int:channel-interceptor>
<task:executor id="executor" pool-size="5"/>
<bean id="requestChannelInterceptor" class="org.springframework.integration.security.channel.SecurityContextPropagationChannelInterceptor"></bean>
<int-http:inbound-gateway id="gateway" request-channel="requestChannel"
path="/service/**"
supported-methods="GET">
</int-http:inbound-gateway>
<int-security:secured-channels access-decision-manager="accessDecisionManager">
<int-security:access-policy pattern="requestChannel" send-access="ROLE_ADMIN,ROLE_USER,ROLE_DBA,ROLE_ANONYMOUS"/>
</int-security:secured-channels>
<bean id="channelService"
class="test.spring.webapp.server.ChannelService"/>
<int:service-activator id="channelServiceActivator"
ref="channelService"
input-channel="requestChannel"
method="manage"/>
</beans>
如您所见,我正在使用 Spring Integration 的组件 SecurityContextPropagationChannelInterceptor,它旨在进行安全上下文的“传播”。
我也在配置中使用int-security:secured-channels,如文档中所示。
但此配置不起作用:当我调用 URL http://localhost:8080/caller/service/hello(记录或未记录相同)时,我收到以下异常:
org.springframework.messaging.MessageHandlingException: HTTP request execution failed for URI [http://localhost:8080/receiver/service/hello];
nested exception is org.springframework.web.client.HttpServerErrorException: 500 Internal Server Error
此外,org.springframework.integration.channel.interceptor.ThreadStatePropagationChannelInterceptor 类有以下方法:
@Override
@SuppressWarnings("unchecked")
public final Message<?> postReceive(Message<?> message, MessageChannel channel) {
if (message instanceof MessageWithThreadState) {
MessageWithThreadState<S> messageWithThreadState = (MessageWithThreadState<S>) message;
Message<?> messageToHandle = messageWithThreadState.message;
populatePropagatedContext(messageWithThreadState.state, messageToHandle, channel);
return messageToHandle;
}
return message;
}
消息实例包含记录的主体。 调试这个方法,我注意到记录的主体(例如“admin”)只在“调用者”端获得,而不是在“接收者”端(总是有“anonymousUser”):为什么传播SecurityContext 不起作用?
我认为我的配置完全错误或缺少某些东西...
【问题讨论】:
标签: spring spring-mvc spring-security spring-integration