Man*_*ojP 4 spring spring-security saml-2.0 spring-saml
我在我的应用程序中配置了spring-saml和spring安全性.我已经给出了不同的url模式来识别请求.如果我在应用程序URL中追加/休息,那么它将创建具有基本身份验证的spring-security上下文.如果我在应用程序URL中附加/ saml,那么它将填充IDP登录页面并在成功登录后重定向到index.html.
但我再次被重定向到login.html页面而不是index.html.在eclipse调试并在此处放置一些日志之后,我得到了没有可用的身份验证对象.
我已阅读此jira链接并将更新的spring-security版本更新为3.1.4.RELEASE但它没有解决我的问题.
After struggling a lot I find that saml security context is getting cleared by filterChainProxy doFilter method and setting authentication is null and then redirecting to the secured target url which requires authentication which is not present. hence it redirect to login page.
I googled a lot but did not find any way to use saml authentication to pass j_spring_security check.
I have attached my saml-security.xml and spring-security.xml file below
saml-security
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- Enable auto-wiring -->
<context:annotation-config/>
<!-- Scan for auto-wiring classes in spring saml packages -->
<context:component-scan base-package="org.springframework.security.saml"/>
<!-- Unsecured pages -->
<security:http security="none" pattern="/favicon.ico"/>
<security:http security="none" pattern="/images/**"/>
<security:http security="none" pattern="/css/**"/>
<security:http security="none" pattern="/logout.jsp"/>
<!-- Filters for processing of SAML messages -->
<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>
<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/index.html"/>
<property name="alwaysUseDefaultTargetUrl" value="true"/>
</bean>
<!--
Use the following for interpreting RelayState coming from unsolicited response as redirect URL:
<bean id="successRedirectHandler" class="org.springframework.security.saml.SAMLRelayStateSuccessHandler">
<property name="defaultTargetUrl" value="/" />
</bean>
-->
<!-- Handler for successful logout -->
<bean id="successLogoutHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
<property name="defaultTargetUrl" value="/login.html"/>
</bean>
<!-- Register authentication manager with SAML provider -->
<security:authentication-manager id="samlAuthenticationManager">
<security:authentication-provider ref="samlAuthenticationProvider"/>
</security:authentication-manager>
<!-- Logger for SAML messages and events -->
<bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/>
<!-- Central storage of cryptographic keys -->
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="file:///${user.home}/conf/samlKeyStore.jks"/>
<constructor-arg type="java.lang.String" value="nalle123"/>
<constructor-arg>
<map>
<entry key="apollo" value="nalle123"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="apollo"/>
</bean>
<!-- Entry point to initialize authentication, default values taken from properties file -->
<bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint">
<property name="defaultProfileOptions">
<bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
<property name="includeScoping" value="false"/>
</bean>
</property>
</bean>
<!-- IDP Discovery Service -->
<bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery">
<!-- <property name="idpSelectionPath" value="/WEB-INF/security/idpSelection.jsp"/> -->
</bean>
<!-- Filter automatically generates default SP metadata -->
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="entityId" value="devenv.abc.com"/>
<property name="signMetadata" value="false"/>
</bean>
</constructor-arg>
</bean>
<!-- The filter is waiting for connections on URL suffixed with filterSuffix and presents SP metadata there -->
<bean id="metadataDisplayFilter" class="org.springframework.security.saml.metadata.MetadataDisplayFilter"/>
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg>
<list>
<bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
<constructor-arg>
<value type="java.lang.String">http://idp.ssocircle.com/idp-meta.xml</value>
</constructor-arg>
<constructor-arg>
<value type="int">500000</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
</list>
</constructor-arg>
</bean>
<!-- SAML Authentication Provider responsible for validating of received SAML messages -->
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
<property name="userDetails" ref="samlUserDetailsService" />
</bean>
<!-- Custom user details service to attach app specific roles to federated identities -->
<bean id="samlUserDetailsService" class="com.mercatus.security.MercatusSAMLUserDetailsService"/>
<!-- Provider of default SAML Context -->
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl"/>
<!-- Processing filter for WebSSO profile messages -->
<bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
<property name="authenticationManager" ref="samlAuthenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
</bean>
<!-- Processing filter for WebSSO Holder-of-Key profile -->
<bean id="samlWebSSOHoKProcessingFilter" class="org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter">
<property name="authenticationManager" ref="samlAuthenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
</bean>
<!-- Logout handler terminating local session -->
<bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
<property name="invalidateHttpSession" value="true"/>
</bean>
<!-- Override default logout processing filter with the one processing SAML messages -->
<bean id="samlLogoutFilter" class="org.springframework.security.saml.SAMLLogoutFilter">
<constructor-arg ref="successLogoutHandler"/>
<constructor-arg ref="logoutHandler"/>
<constructor-arg ref="logoutHandler"/>
</bean>
<!-- Filter processing incoming logout messages -->
<!-- First argument determines URL user will be redirected to after successful global logout -->
<bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.SAMLLogoutProcessingFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
</bean>
<!-- Class loading incoming SAML messages from httpRequest stream -->
<bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg>
<list>
<ref bean="redirectBinding"/>
<ref bean="postBinding"/>
<ref bean="artifactBinding"/>
<ref bean="soapBinding"/>
<ref bean="paosBinding"/>
</list>
</constructor-arg>
</bean>
<!-- SAML 2.0 WebSSO Assertion Consumer -->
<bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/>
<!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
<bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 Web SSO profile -->
<bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/>
<!-- SAML 2.0 Holder-of-Key Web SSO profile -->
<bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 ECP profile -->
<bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/>
<!-- SAML 2.0 Logout Profile -->
<bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"/>
<!-- Bindings, encoders and decoders used for creating and parsing messages -->
<bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
</bean>
<bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="artifactBinding" class="org.springframework.security.saml.processor.HTTPArtifactBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
<constructor-arg>
<bean class="org.springframework.security.saml.websso.ArtifactResolutionProfileImpl">
<constructor-arg>
<bean class="org.apache.commons.httpclient.HttpClient">
<constructor-arg>
<bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
</constructor-arg>
</bean>
</constructor-arg>
<property name="processor">
<bean class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg ref="soapBinding"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<bean id="soapBinding" class="org.springframework.security.saml.processor.HTTPSOAP11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="paosBinding" class="org.springframework.security.saml.processor.HTTPPAOS11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<!-- Initialization of OpenSAML library-->
<bean class="org.springframework.security.saml.SAMLBootstrap"/>
<!-- Initialization of the velocity engine -->
<bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory" factory-method="getEngine"/>
<!-- XML parser pool needed for OpenSAML parsing -->
<bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize">
<property name="builderFeatures">
<map>
<entry key="http://apache.org/xml/features/dom/defer-node-expansion" value="false"/>
</map>
</property>
</bean>
<bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/>
</beans>
Run Code Online (Sandbox Code Playgroud)
And my spring-security.xml file given below
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:oauth2="http://www.springframework.org/schema/security/oauth2"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/security/oauth2
http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd">
<aop:aspectj-autoproxy/>
<!-- Definition for logging aspect -->
<bean id="assumptionAuditLogAspect" class="com.mercatus.audit.AssumptionAuditLogAspect"/>
<!-- Definition for project security aspect -->
<bean id="projectSecurityAspect" class="com.mercatus.web.security.ProjectSecurityAspect"/>
<!--Definition for SavedRequestAwareAuthenticationSuccessHandler -->
<bean id="mercatusSavedRequestHandler" class="com.mercatus.security.MercatusSavedRequestHandler"/>
<bean id="mercatusLogoutSuccessHandler" class="com.mercatus.security.MercatusLogoutSuccessHandler"/>
<bean id="mercatusAjaxTimeoutFilter" class="com.mercatus.security.MercatusAjaxTimeoutFilter"/>
<security:http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager">
<security:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<security:anonymous enabled="false" />
<security:http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<security:custom-filter ref="clientCredentialsTokenEndpointFilter"
after="BASIC_AUTH_FILTER" />
<security:access-denied-handler ref="oauthAccessDeniedHandler" />
</security:http>
<!-- SAML starts -->
<security:http pattern="/saml/**" entry-point-ref="samlEntryPoint">
<security:intercept-url pattern="/oauth/**" access="ROLE_USER" />
<security:intercept-url pattern="/rest/**" access="ROLE_USER" />
<security:intercept-url pattern="/saml" access="IS_AUTHENTICATED_FULLY"/>
<security:anonymous enabled="false" />
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</security:http>
<!-- SAML ends -->
<security:http pattern="/rest/**" access-decision-manager-ref="accessDecisionManager">
<security:anonymous enabled="false" />
<security:form-login login-page="/login.html" authentication-success-handler-ref="mercatusSavedRequestHandler"
authentication-failure-url="/login.jsp?login_error=true"/>
<security:intercept-url pattern="/rest/**" access="ROLE_USER" />
<security:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<security:custom-filter ref="mercatusAjaxTimeoutFilter" after="EXCEPTION_TRANSLATION_FILTER"/>
<security:access-denied-handler ref="oauthAccessDeniedHandler"/>
</security:http>
<security:http access-denied-page="/login.jsp?login_error=true">
**<security:intercept-url pattern="/index.html" access="ROLE_USER" />**
<security:intercept-url pattern="/saml/**" access="ROLE_USER" />
<security:intercept-url pattern="/oauth/**" access="ROLE_USER" />
<security:intercept-url pattern="/customer/*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:form-login login-page="/login.html" authentication-success-handler-ref="mercatusSavedRequestHandler"
authentication-failure-url="/login.jsp?login_error=true"/>
<security:logout delete-cookies="true" invalidate-session="true" logout-success-url="/login.html"/>
<security:anonymous />
</security:http>
<security:authentication-manager id="clientAuthenticationManager">
<security:authentication-provider user-service-ref="clientDetailsUserService" />
</security:authentication-manager>
<oauth2:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices"
user-approval-handler-ref="userApprovalHandler">
<oauth2:authorization-code />
<oauth2:implicit />
<oauth2:refresh-token />
<oauth2:client-credentials />
<oauth2:password />
</oauth2:authorization-server>
<oauth2:resource-server id="resourceServerFilter"
resource-id="mercatus" token-services-ref="tokenServices" />
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
<constructor-arg>
<list>
<bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>
<security:global-method-security pre-post-annotations="enabled"/>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="mercatusAuthenticationProvider" />
</security:authentication-manager>
<bean id="mercatusAuthenticationProvider" class="com.mercatus.security.MercatusAuthenticationProvider" />
</beans>
Run Code Online (Sandbox Code Playgroud)
Can anyone help me to solve this issue. Thanks in advance.
经过近一周的挣扎,最后我解决了这个问题.
在通过eclipse进行调试时,我发现SAMLAuthenticationProvider中的根本原因是有一个getEntitlements导致问题的方法.
protected Collection<? extends GrantedAuthority> getEntitlements(SAMLCredential credential, Object userDetail) {
if (userDetail instanceof UserDetails) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.addAll(((UserDetails) userDetail).getAuthorities());
return authorities;
} else {
return Collections.emptyList();
}
}
Run Code Online (Sandbox Code Playgroud)
这里检查userDetail对象是否是一个instanceOf UserDetails类然后返回所有权限列表,否则将返回空权限列表.
基于表单的身份验证可以返回UserDetails对象,但是如果用户通过IDP登录启动了SSO,则UsernamePasswordAuthenticationToken返回类型的对象.因此,使用userDetail对象获取grantAuthourity的空列表.
所以我扩展了SAMLAuthenticationProvider我的应用程序并覆盖以下方法
@Override
public Collection<? extends GrantedAuthority> getEntitlements(SAMLCredential credential, Object userDetail)
{
logger.info("****** object is instance of UserDetails :"+ (userDetail instanceof UserDetails));
if (userDetail instanceof UserDetails)
{
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.addAll(((UserDetails) userDetail).getAuthorities());
return authorities;
}
else if(userDetail instanceof UsernamePasswordAuthenticationToken)
{
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.addAll(((UsernamePasswordAuthenticationToken) userDetail).getAuthorities());
return authorities;
} else {
return Collections.emptyList();
}
}
Run Code Online (Sandbox Code Playgroud)
然后,我使用自定义SAMLUserDetailsService类引用将自定义authenticationProvider引用提供给saml-security.xml文件.
<bean id="samlAuthenticationProvider" class="com.mercatus.security.MercatusSAMLAuthenticationProvider">
<property name="userDetails" ref="samlUserDetailsService" />
</bean>
<bean id="samlUserDetailsService" class="com.mercatus.security.MercatusSAMLUserDetailsService"/>
Run Code Online (Sandbox Code Playgroud)
上面的配置救了我.登录后我可以访问受保护的资源.
我在FilterChainProxy里面花了整整一周的时间调试,还有很多其他的过滤器,因为intercepter URL它被重定向到FilterChainProxy.
我发布详细信息,因为它可能对面临类似问题的其他人有所帮助.
| 归档时间: |
|
| 查看次数: |
5241 次 |
| 最近记录: |