Ошибка безопасности Spring при удаленном вызове из встроенного приложения Flex на странице Spring MVC - PullRequest
0 голосов
/ 17 мая 2011

Хотелось бы, чтобы кто-нибудь помог мне с этим.У меня есть приложение Spring MVC (Spring 3), прекрасно работающее с Spring Security 3, теперь мы добавляем поддержку Flex и добавляем BlazeDS в приложение и Spring Integration (1.5.0 M2), все начали работать нормально, пока мы не захотели интегрироватьсяаутентификация через Spring Security.Приложение Flex представляет собой «мини» пользовательский интерфейс, который служит в качестве чата P2P (посредством обмена сообщениями) между двумя пользователями и встроен в страницу JSP в приложении Spring MVC, что мы хотим сделать, это убедиться (из приложения Flex), чтопользователь вошел в систему, прежде чем показывать интерфейс чата.Аутентификация выполняется из приложения Spring MVC (веб-формы) и работает нормально, но каждый раз, когда мы обращаемся к странице Spring MVC, содержащей приложение Flex, и выполняем удаленный вызов из Flex, чтобы получить данные текущего пользователя, мы получаемисключение:

flex.messaging.security.SecurityException: An Authentication object was not found in the SecurityContext

Мы предполагали, что запрос удаленного взаимодействия (сделанный из аутентифицированного сеанса) будет каким-то образом получен и распознан и что клиенту Flex не нужно будет проходить аутентификацию снова.Что здесь может быть не так?Вот моя весенняя конфигурация безопасности и мой файл конфигурации flex, а также web.xml:

security.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>

web.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));

Предыдущий код всегда заканчивается обработчиком ошибок с упомянутой ошибкойв начале запроса

Другие файлы конфигурации:

services-config.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>

messaging-config.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>

Ответы [ 2 ]

3 голосов
/ 18 мая 2011

После некоторых копаний в конфигурации я нашел проблему, и я выложу решение для всех, кому это может понадобиться.Мне пришлось явно сказать Spring Security, чтобы включить «securityContextPersistenceFilter» в цепочку фильтров для моих URL «/ messagebroker / **», чтобы контекст безопасности был правильно заполнен информацией для аутентификации (как мы предполагали с самого начала).Конфигурация была добавлена ​​в bean-компонент «springSecurityFilterChain» следующим образом:

<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 Security все запросы из пользовательского интерфейса Flex автоматически заполняются существующей аутентификацией, созданной из предыдущей аутентификации.войдите через веб-форму Spring MVC.

0 голосов
/ 17 мая 2011

Чтобы дать быстрый ответ - можете ли вы настроить свои Flex-компоненты для совмещения в существующей форме входа в Spring Security на основе HTML?Да, безусловно, вы можете.Я сделал это, и он работает довольно хорошо, если у вас есть какое-то приложение, которое нужно защитить, но не обслуживать через гибкое приложение.

Я НЕ использую

<flex:secured> 

часть конфигурации брокера сообщений (она не существовала, когда я впервые ее настраивал, возможно, я мог бы переключиться на нее сейчас, но, поскольку мои настройки работают, я не испытываю особой необходимости в данный момент).У меня настроен шаблон URL моего MessageBroker для защиты.Мой брокер сообщений защищен следующим образом:

(страница загрузки 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, вы можете установить точку останова для этого метода и заглянуть в контекст безопасности.и убедитесь, что он правильно заполнен (но кажется, что это не так - что странно)

...