Spring Security и вложенный FilterChainProxy пишут SAML Service Provider

Я пытаюсь выяснить проблему, связанную с Spring Security и SAML. Мы пытаемся использовать Spring Security (spring-security-core-3.1.1.RELEASE.jar) и SAML (spring-security-saml2-core-1.0.0-RC1-SNAPSHOT.jar), чтобы изменить наш продукт, чтобы он САМЛ СП. РЕДАКТИРОВАТЬ: Вот (я думаю!) соответствующий раздел моего связанного с безопасностью контекста xml. Как видите, он почти идентичен этот образец XML.

<!-- Entry point to initialize authentication, default values taken from properties file -->
<bean id="samlEntryPoint" class="com.myproduct.samlsp.impl.PSSAMLEntryPoint">
    <property name="defaultProfileOptions">
        <bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
            <property name="includeScoping" value="false"/>
        </bean>
    </property>
</bean>

<!-- Unsecured pages -->
<security:http security="none" pattern="/saml/web/**"/>
<security:http security="none" pattern="/logout.jsp"/>
<security:http security="none" pattern="/favicon.ico"/>
<security:http security="none" pattern="/images/**"/>
<security:http security="none" pattern="/scripts/**"/>
<security:http security="none" pattern="/flash/**"/>
<security:http security="none" pattern="/loggedout.html"/>

<!-- Secured pages -->
<security:http entry-point-ref="samlEntryPoint">
    <security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
    <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
    <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</security:http>

<!-- IDP Discovery Service -->
<bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery">
    <property name="idpSelectionPath" value="/WEB-INF/security/idpSelection.jsp"/>
</bean>

<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
    <security:filter-chain-map request-matcher="ant">
        <security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
        <security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
        <security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
        <security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
        <security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
        <security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
        <security:filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/>
    </security:filter-chain-map>
</bean>

Симптом заключается в том, что сразу после аутентификации с помощью IDP страница моего поставщика услуг отображается правильно; однако изменение URL-адреса (например, нажатие любой ссылки) немедленно отправляет меня обратно к IDP. Кажется, я понял, почему, но я не знаю, почему это не всегда так.

Мое понимание Spring Security заключается в том, что проверка авторизации основана на SecurityContextHolder. А именно, поместите объект Authentication на держатель, и все вокруг него будет проверять подлинность. Затем SecurityContextPersistenceFilter отвечает за поддержание соответствия репозитория сеансов.

Итак, когда я прослеживаю код безопасности Spring, я вижу SecurityContextPersistenceFilter со следующим кодом:

SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
try {
  SecurityContextHolder.setContext(contextBeforeChainExecution);
  chain.doFilter(holder.getRequest(), holder.getResponse());
} finally {
  SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
  // Crucial removal of SecurityContextHolder contents - do this before anything else.
  SecurityContextHolder.clearContext();
  repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
  ....
}

Все идет нормально. Затем я смотрю на FilterChainProxy и нахожу:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    try {
        doFilterInternal(request, response, chain);
    } finally {
        // SEC-1950
        SecurityContextHolder.clearContext();  <------- Key line here
    }
}

Это все еще кажется в порядке. Поскольку FilterChainProxy должен вызываться только один раз в основе всех фильтров Spring Security, в этот момент не проблема очистить SecurityContextHolder.

ОДНАКО, это не то, что происходит. На самом деле происходит то, что clearContext в FilterChainProxy вызывается ДО того, как SecurityContextPersistenceFilter получает возможность прочитать его из контекста в contextAfterChainExecution. Это происходит потому, что FilterChainProxy фактически дважды встречается в цепочке вызовов. Я знаю это, потому что я установил точку останова в FilterChainProxy.doFilter, и она вызывается дважды. При первом вызове он имеет еще один экземпляр FilterChainProxy в своей FilterChain. Вот стек фильтров, возвращаемый методом getFilters FilterChainProxy:

org.springframework.security.saml.metadata.MetadataGeneratorFilter@78104d3c
org.springframework.security.web.context.SecurityContextPersistenceFilter@168c795e
FilterChainProxy[ Filter Chains: [ .... my patterns ] ],
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@7fffde92
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@e2d09d7
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1c2b968f
org.springframework.security.web.session.SessionManagementFilter@395f222a
org.springframework.security.web.access.ExceptionTranslationFilter@372e6f09
org.springframework.security.web.access.intercept.FilterSecurityInterceptor@7dab91aa

С этой цепочкой фильтров я не понимаю, как SecurityContextPersistenceFilter вообще может работать: кажется, что SecurityContextHolder всегда будет очищаться, прежде чем он получит возможность сохранить его.

Здесь что-то явно не так? Я что-то неправильно понимаю в Spring Security (очень возможно!)


person fool4jesus    schedule 14.06.2013    source источник


Ответы (1)


Мне не удалось получить какое-либо окончательное утверждение об этом, но проблема, похоже, заключается в том, что Spring Security 3.1.1 плохо работает с Spring SAML или любой реализацией, которая использует тот же тип вложенных FilterChainProxys. Похоже, что FilterChainProxy был полностью переписан для версии 3.1.1. Когда я просматривал последнюю версию (3.1.4), я заметил, что в предложении finally есть проверка, которая очищает SecurityContextHolder ("SEC-1950") только в том случае, если это первый вызов фильтра.

Таким образом, обновление весенней безопасности до 3.1.4 решило проблему.

person fool4jesus    schedule 19.06.2013
comment
Спасибо за публикацию решения. Я столкнулся с той же проблемой, что и ваше решение сработало для меня. - person Mahesh Gosemath; 12.02.2014
comment
Рад слышать, что я не единственный, кто это видел. Спасибо за продолжение! - person fool4jesus; 13.02.2014
comment
Я сталкиваюсь с той же проблемой даже после обновления весенней версии безопасности до 3.1.4.RELEASE. - person ManojP; 30.03.2015