打开和关闭 Spring Security SAML 的方法
Posted
技术标签:
【中文标题】打开和关闭 Spring Security SAML 的方法【英文标题】:Ways to toggle spring security SAML on and off 【发布时间】:2014-09-30 01:09:03 【问题描述】:除了其他身份验证机制外,我的应用程序中还有一个非常标准的 spring security saml 实现。开箱即用的 SAML 不会被配置,但可以通过表单进行配置,因此默认情况下应该禁用 SAML。我希望能够轻松地打开/关闭 SAML,但不确定最好的方法是什么。
似乎一种方法是执行自定义 FilterChainProxy,如果我检查是否启用了 saml,则忽略 samlFilter 链(How to delete one filter from default filter stack in Spring Security?),并对元数据生成器过滤器执行类似的实现。
任何建议都会很棒。
这是我的配置:
<http auto-config="false" use-expressions="true"
access-decision-manager-ref="webAccessDecisionManager"
disable-url-rewriting="false"
create-session="never"
authentication-manager-ref="authenticationManager">
<custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</http>
元数据生成器过滤器:
<beans:bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<beans:constructor-arg>
<beans:bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<beans:property name="entityId" value="$saml.entityId"/>
<beans:property name="signMetadata" value="$saml.signMetadata"/>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
萨姆过滤器:
<beans:bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<filter-chain-map request-matcher="ant">
<filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
<filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
<filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
<filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
<filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
<filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/>
</filter-chain-map>
</beans:bean>
编辑:这是我的实现,它有点骇人听闻,并且依赖于不推荐使用的方法,但它可以工作
下面的 sn -p 禁用 MetadataGeneratorFilter:
public class MyMetadataGeneratorFilter extends MetadataGeneratorFilter
private boolean isActive = false;
public MyMetadataGeneratorFilter(MetadataGenerator generator)
super(generator);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
if (isActive)
processMetadataInitialization((HttpServletRequest) request);
chain.doFilter(request, response);
public void setActive(boolean active)
isActive = active;
还有自动装配的 samlFilter / FilterChainMap。如果启用了 saml,我会保留此链,如果它被禁用,我会在我的服务中将链设置为启用/禁用 saml 的空映射。
初始化后,我得到 filterchainmap 值:
private Map<RequestMatcher, List<Filter>> map;
@Override
public void init() throws ServiceException, MetadataProviderException
SamlConfig samlConfig = getConfig();
map = samlFilter.getFilterChainMap();
applySamlConfig(samlConfig);
在下面的方法中,我将过滤器链映射设置为 spring xml 中提供的原始映射(如果启用)或空映射(如果禁用)。
public void applySamlConfig(SamlConfig samlConfig) throws ServiceException, MetadataProviderException
if (!samlConfig.isEnabled())
Map<RequestMatcher, List<Filter>> emptyMap = samlFilter.getFilterChainMap();
emptyMap.clear();
samlFilter.setFilterChainMap(emptyMap);
return;
samlFilter.setFilterChainMap(map);
【问题讨论】:
您是否打算在实时应用程序上执行此操作,或者您有能力更改设置并重新启动服务器? @dharam,理想情况下您不必重新启动,但必须重新启动是可以接受的。 @TheTurkish。你解决了这个问题吗?现在我想要与您期望的相同的行为,但我使用的是注释而不是 xml,您能告诉我您是如何解决这个问题的吗? 【参考方案1】:我在 entry-point-ref 定义中添加了一个自定义过滤器。如果该功能未启用,此过滤器会跳过所有后续过滤器。
<security:http entry-point-ref="samlEntryPoint">
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<!-- This filter checks if the SSO-Feature is enabled - otherwise all following security filters will be skipped -->
<security:custom-filter before="BASIC_AUTH_FILTER" ref="ssoEnabledFilter"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter" />
<security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter" />
ssoEnabledFilter:
public class SsoEnabledFilter extends GenericFilterBean
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException
boolean ssoEnabled = isSsoEnabled();
if (ssoEnabled)
filterChain.doFilter(request, response);
else
request.getRequestDispatcher(((HttpServletRequest) request).getServletPath()).forward(request, response);
【讨论】:
我试过上面提到的@David 的解决方案,结果出现以下错误 org.apache.catalina.core.ApplicationDispatcher.invoke Servlet.service() for servlet [default] throw exception java.lang.***Error The问题似乎与请求处理期间的无限过滤有关。http.httpBasic().authenticationEntryPoint(samlEntryPoint()); http.addFilterBefore(CheckSAMLConf(), BasicAuthenticationFilter.class); http.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class); http.addFilterAfter(springSecurityFilter(), BasicAuthenticationFilter.class);
class SsoEnabledFilter extends GenericFilterBean @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException boolean ssoEnabled = false; if (ssoEnabled) filterChain.doFilter(request, response); else request.getRequestDispatcher(((HttpServletRequest) request).getServletPath()).forward(request, response);
【参考方案2】:
到目前为止,我一直在使用自定义 Spring 命名空间来实现这一点,该命名空间根据后端配置包含或跳过某些 bean,并在后端配置发生更改时重新加载 Spring 上下文。
【讨论】:
您能否分享您的实现细节或指出我对自定义 Spring 命名空间的更多阅读?我猜这需要重新启动应用程序? 这里有几个教程:cscarioni.blogspot.fi/2012/04/…,docs.spring.io/spring/docs/3.1.0.M1/spring-framework-reference/…,它不需要重启,因为你可以调用 ApplicationContext.refresh() 来重新加载你的 bean。但我相信有更简单的方法,以防您只想启用/禁用过滤器。自定义命名空间允许对整个 Spring SAML 进行更多动态配置,而不是启用/禁用。但实施需要时间。【参考方案3】:编辑:修复了 TheTurkish 提示的错误
如果您希望能够在正在运行的应用程序上切换 SAML 的使用,更简单的方法是使用 samlFilter 的包装器。例如
public class FilterWrapper extends GenericFilterBean
private Filter inner;
private boolean active;
private boolean targetFilterLifeCycle = false;
public Filter getInner()
return inner;
public void setInner(Filter inner)
this.inner = inner;
public boolean isActive()
return active;
public void setActive(boolean active)
this.active = active;
@Override
public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException
if (active)
inner.doFilter(sr, sr1, fc);
else
fc.doFilter(str,sr1);
@Override
protected void initFilterBean() throws ServletException
super.initFilterBean();
if (inner == null)
throw new ServletException("Inner cannot be null");
if (targetFilterLifeCycle)
inner.init(getFilterConfig());
@Override
public void destroy()
super.destroy();
if (inner != null && targetFilterLifeCycle)
inner.destroy();
你可以这样使用它:
<bean id="samlFilter" class="...FilterWrapper" p:active="false">
<property name=inner>
<!-- the real samlFilter bean -->
<bean class="org.springframework.security.web.FilterChainProxy">
...
</bean>
</property>
</bean>
因为它是一个 bean,你将它注入到你想要激活/停用 Saml 和简单调用的地方:
samlFilter.setActive(active);
【讨论】:
嗯。这似乎不起作用。它看起来像 if inner.doFilter(sr, sr1, fc);不调用,过滤器被完全杀死,应用程序不返回响应。以上是关于打开和关闭 Spring Security SAML 的方法的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security SAML Extension 将 ADFS 3.0 与其他声明提供程序集成