Spring Security和嵌套的FilterChainProxy编写SAML服务提供程序

foo*_*sus 7 spring spring-security saml-2.0 spring-saml

我试图找出涉及Spring Security和SAML的问题.我们正在尝试使用Spring Security(spring-security-core-3.1.1.RELEASE.jar)和SAML(spring-security-saml2-core-1.0.0-RC1-SNAPSHOT.jar)来修改我们的产品SAML SP.编辑:这是我的安全相关上下文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>
Run Code Online (Sandbox Code Playgroud)

症状是,在使用IDP进行身份验证后,我的SP页面会立即显示; 但是,更改URL(例如,单击任何链接)会立即将我发送回IDP.我想我已经找到了原因,但我不知道为什么情况并非如此.

我对Spring Security的理解是授权检查都是基于SecurityContextHolder.也就是说,将一个Authentication对象放在持有者身上,一切都会围绕它进行身份验证.然后SecurityContextPersistenceFilter负责维护会话存储库以匹配.

因此,当我浏览Spring Security代码时,我看到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());
  ....
}
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.然后,我看看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
    }
}
Run Code Online (Sandbox Code Playgroud)

这似乎还可以.由于FilterChainProxy只应在所有Spring Security过滤器的基础上调用一次,因此在此时清除SecurityContextHolder并不是一个问题.

但是,这不是正在发生的事情.实际发生的是FilterChainProxy中的clearContext被称为BEFORE,SecurityContextPersistenceFilter有机会将它从上下文中读入contextAfterChainExecution.之所以发生这种情况,是因为FilterChainProxy实际上在调用链中出现了两次.我知道这是因为我在FilterChainProxy.doFilter中设置了一个断点,并且它被调用了两次.它第一次被调用时,它的FilterChain中有另一个FilterChainProxy实例.这是FilterChainProxy的getFilters方法返回的Filter堆栈:

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
Run Code Online (Sandbox Code Playgroud)

有了这个过滤器链,我不明白SecurityContextPersistenceFilter如何工作:看起来SecurityContextHolder在它有机会持久化之前总会被清除.

这里有什么明显的错误吗?我是否误解了Spring Security中的某些内容(非常可能!)

foo*_*sus 4

我无法得到任何明确的说法,但问题似乎是 Spring Security 3.1.1 不能很好地与 Spring SAML 或任何使用相同类型的嵌套 FilterChainProxy 的实现配合使用。FilterChainProxy 似乎已针对 3.1.1 进行了完全重写。当我查看最新版本(3.1.4)时,我注意到finally子句中有一个检查,仅在第一次调用过滤器时才清除SecurityContextHolder(“SEC-1950”)。

因此,将spring security升级到3.1.4解决了这个问题。