我希望有人能帮我解决这个问题。我有一个Spring MVC应用程序(Spring 3)在Spring Security 3上运行良好,我们现在正在添加对Flex的支持,并将BlazeDS添加到应用程序和Spring Integration(1.5.0 M2)中,所有这些都开始工作正常,直到我们想通过Spring Security集成身份验证。Flex应用程序是一个"迷你"UI,充当两个用户之间的P2P聊天(通过消息传递),它嵌入在Spring MVC应用程序的JSP页面中,我们要做的是确保(从Flex应用程序)用户在显示聊天UI之前已登录。身份验证是从Spring MVC应用程序(Web表单)完成的,并且工作正常,但是每次我们访问包含Flex应用程序的Spring MVC页面并从Flex进行远程调用以获取当前用户详细信息时,我们都会得到一个异常:
flex.messaging.security.SecurityException: An Authentication object was not found in the SecurityContext
我们假设远程处理请求(从经过身份验证的会话发出)将以某种方式被拾取和识别,并且 Flex 客户端不需要再次进行身份验证。这里可能出了什么问题?这是我的 spring 安全配置和我的 flex 配置文件以及 web.xml:
安全性.xml:
<bean id="springSecurityFilterChain"
class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain filters="none" pattern="/styles/**" />
<sec:filter-chain filters="none" pattern="/js/**" />
<sec:filter-chain filters="none" pattern="/images/**" />
<sec:filter-chain
filters="securityContextPersistenceFilter,
logoutFilter,
usernamePasswordAuthenticationFilter,
anonymousAuthenticationFilter,
exceptionTranslationFilter,
menuLoaderRequestFilter,
filterSecurityInterceptor"
pattern="/web/**" />
<sec:filter-chain
filters="securityContextPersistenceFilter,
usernamePasswordAuthenticationFilter,
exceptionTranslationFilter"
pattern="/do_login" />
<sec:filter-chain
filters="securityContextPersistenceFilter,
logoutFilter,
exceptionTranslationFilter"
pattern="/do_logout" />
</sec:filter-chain-map>
</bean>
<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter" />
<bean id="usernamePasswordAuthenticationFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager"
ref="authenticationManager" />
<property name="filterProcessesUrl" value="/do_login"/>
<property name="authenticationFailureHandler">
<ref bean="loginFailureHandler" />
</property>
<property name="authenticationSuccessHandler">
<ref bean="loginSuccessHandler" />
</property>
<property name="usernameParameter" value="login_user" />
<property name="passwordParameter" value="login_password" />
</bean>
<bean id="loginFailureHandler"
class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/web/login?error=login.failure"/>
<property name="exceptionMappings">
<map>
<entry>
<key>
<value>org.springframework.security.authentication.AuthenticationServiceException</value>
</key>
<value>/web/login?error=login.database.failure</value>
</entry>
</map>
</property>
</bean>
<bean id="loginSuccessHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/web/index"/>
</bean>
<bean id="anonymousAuthenticationFilter"
class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
<property name="userAttribute"
value="anonymousUser,ROLE_ANONYMOUS" />
<property name="key" value="AD17JFJ005P00Z7MK" />
</bean>
<bean id="logoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<!-- the post-logout destination -->
<constructor-arg value="/web/login?success=login.loggedout"/>
<constructor-arg>
<array>
<ref bean="logoutHandler" />
</array>
</constructor-arg>
<property name="filterProcessesUrl" value="/do_logout"/>
</bean>
<bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
<bean id="exceptionTranslationFilter"
class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint"
ref="mainEntryPoint"/>
<property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</bean>
<bean id="mainEntryPoint"
class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
<constructor-arg>
<map>
<entry>
<key>
<value>hasHeader('X-Requested-With', 'XMLHttpRequest')</value>
</key>
<ref bean="ajaxEntryPoint"/>
</entry>
<entry>
<key>
<value>hasHeader('Content-type', 'application/x-amf')</value>
</key>
<ref bean="flexEntryPoint" />
</entry>
</map>
</constructor-arg>
<property name="defaultEntryPoint" ref="defaultEntryPoint" />
</bean>
<bean id="entryPointTemplate" abstract="true">
<property name="loginFormUrl" value="/web/login"/>
</bean>
<bean id="ajaxEntryPoint" parent="entryPointTemplate"
class="com.saes.support.security.AjaxAuthenticationEntryPoint" >
</bean>
<bean id="defaultEntryPoint" parent="entryPointTemplate"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
</bean>
<bean id="flexEntryPoint" class="org.springframework.flex.security3.FlexAuthenticationEntryPoint">
</bean>
<bean id="accessDeniedHandler"
class="com.saes.support.security.SAESAccessDeniedHandler">
<property name="errorPage"
value="/web/errors/accessDenied"/>
</bean>
<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager"
ref="authenticationManager"/>
<property name="accessDecisionManager" ref="decisionManager"/>
<property name="securityMetadataSource">
<sec:filter-security-metadata-source>
<sec:intercept-url pattern="/web/profile/**"
access="ROLE_USER" />
<sec:intercept-url pattern="/web/doctor/**"
access="ROLE_DOCTOR" />
</sec:filter-security-metadata-source>
</property>
</bean>
<bean id="menuLoaderRequestFilter"
class="com.saes.security.menu.MenuPermissionsAdapterRequestFilter">
</bean>
<bean id="decisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false" />
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
<ref bean="authenticatedVoter"/>
</list>
</property>
</bean>
<bean id="roleVoter"
class="org.springframework.security.access.vote.RoleVoter" />
<bean id="authenticatedVoter"
class="org.springframework.security.access.vote.AuthenticatedVoter" />
<bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService">
<ref bean="userDetailsService" />
</property>
</bean>
<bean id="anonymousAuthenticationProvider"
class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
<property name="key" value="AD17JFJ005P00Z7MK"/>
</bean>
<bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider" />
<ref bean="anonymousAuthenticationProvider" />
</list>
</property>
</bean>
flex-servlet.xml:
<flex:message-broker
services-config-path="/WEB-INF/config/flex/services-config.xml">
<flex:secured authentication-manager="authenticationManager"
access-decision-manager="decisionManager">
<flex:secured-endpoint-path pattern="**/messagebroker/*" access="ROLE_USER"/>
</flex:secured>
</flex:message-broker>
网站.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/spring/persistence.xml
/WEB-INF/config/spring/security.xml
/WEB-INF/config/spring/services.xml
/WEB-INF/config/spring/facade.xml
/WEB-INF/config/spring/validator.xml
/WEB-INF/config/flex/flex-context.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>mainServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mainServlet</servlet-name>
<url-pattern>/web/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>flex</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>flex</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/jsp/errors/critical-error.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/errors/404.jsp</location>
</error-page>
以下是 Flex 代码的相关部分:
<s:ChannelSet id="chatChannelSet">
<s:StreamingAMFChannel url="http://192.168.1.3:8080/MyApp/messagebroker/streamamf">
</s:StreamingAMFChannel>
</s:ChannelSet>
<s:ChannelSet id="remotingChannelSet">
<s:AMFChannel url="http://192.168.1.3:8080/MyApp/messagebroker/amf">
</s:AMFChannel>
</s:ChannelSet>
<s:RemoteObject id="remoteService"
destination="remoteService"
channelSet="{remotingChannelSet}">
</s:RemoteObject>
var asyncCall:AsyncToken = remoteService.getTicketForCurrentUser();
asyncCall.addResponder(new Responder(getTicket_Result, getTicket_Fault));
前面的代码总是在错误处理程序中结束,并在提示的开头提到错误
其他配置文件:
服务配置.xml:
<services-config>
<services>
<service-include file-path="messaging-config.xml" />
</services>
<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
<channel-definition id="streaming-amf" class="mx.messaging.channels.StreamingAMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/streamamf" class="flex.messaging.endpoints.StreamingAMFEndpoint"/>
<properties>
<idle-timeout-minutes>0</idle-timeout-minutes>
<max-streaming-clients>10</max-streaming-clients>
<server-to-client-heartbeat-millis>5000</server-to-client-heartbeat-millis>
<user-agent-settings>
<user-agent match-on="MSIE" kickstart-bytes="2048" max-streaming-connections-per-session="3" />
<user-agent match-on="Firefox" kickstart-bytes="2048" max-streaming-connections-per-session="3" />
</user-agent-settings>
</properties>
</channel-definition>
</channels>
<logging>
<target class="flex.messaging.log.ConsoleTarget" level="Debug">
<properties>
<prefix>[BlazeDS] </prefix>
<includeDate>false</includeDate>
<includeTime>false</includeTime>
<includeLevel>false</includeLevel>
<includeCategory>false</includeCategory>
</properties>
<filters>
<pattern>Endpoint.*</pattern>
<pattern>Service.*</pattern>
<pattern>Configuration</pattern>
</filters>
</target>
</logging>
<system>
<redeploy>
<enabled>false</enabled>
</redeploy>
</system>
消息配置.xml:
<service id="message-service"
class="flex.messaging.services.MessageService">
<adapters>
<adapter-definition id="actionscript" class="flex.messaging.services.messaging.adapters.ActionScriptAdapter" default="true" />
</adapters>
<destination id="chat-destination">
<properties>
<server>
<message-time-to-live>0</message-time-to-live>
<allow-subtopics>true</allow-subtopics>
<subtopic-separator>.</subtopic-separator>
<disallow-wildcard-subtopics>true</disallow-wildcard-subtopics>
</server>
</properties>
<channels>
<channel ref="streaming-amf" />
</channels>
</destination>
<default-channels>
<channel ref="streaming-amf"/>
</default-channels>
在深入研究配置后,我发现了问题,我将为可能需要它的每个人发布解决方案。我必须明确地告诉Spring Security在我的"/messagebroker/**"URL的过滤器链中包含"securityContextPersistenceFilter",以便安全上下文中正确填充身份验证信息(正如我们从一开始就假设的那样)。该配置被添加到"springSecurityFilterChain"bean中,如下所示:
<bean id="springSecurityFilterChain"
class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<!-- other filter chain maps and options here (see the entire file in comment above -->
<sec:filter-chain
filters="securityContextPersistenceFilter"
pattern="/messagebroker/**" />
</bean>
将该过滤器链配置添加到 Spring 安全性后,过滤来自 Flex UI 的所有请求,其中自动填充了通过 Spring MVC Web 表单从上次登录创建的现有身份验证。
为了给出一个快速的答案 - 你能把你的 Flex 东西设置成搭载在现有的基于 Spring Security HTML 的登录表单上吗?是的,绝对可以。我已经做到了,如果您有一些应用程序需要保护但无法通过 flex 应用程序提供服务,它的效果非常好。
我没有使用
<flex:secured>
消息代理配置的一部分(当我第一次设置它时它不存在,也许我现在可以切换到它,但由于我的设置正在工作,我目前不觉得有任何强烈需求)。我已将我的消息代理 URL 模式设置为安全。我的消息代理是安全的,如下所示:
(SWF 加载页面只是包装 SWF 的页面的占位符名称...和 .do 映射到我的视图解析器)
<security:intercept-url pattern="/swf-loading-page.do*" access="ROLE_USER,
<security:intercept-url pattern="/messagebroker/**" access="ROLE_USER" />
在弹性方面,您调用:
remoteService.getTicketForCurrentUser()
在 java 端的服务器上调用什么?
对于我的,我做了类似的事情 - 我拉出本地用户将其返回到 flex 客户端(显示当前登录的用户等) -
在Java端,您可以使用SpringSecurityContextHolder获取SecurityContext,然后从SecurityContext中提取
//These packages are where you can hook into the authentication from your service
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
...
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
如果您是具有jpda调试的应用程序服务器,则可以在该方法上设置断点并查看安全上下文并确保它已正确填充(但似乎不是 - 这很奇怪)