我们有ADFS服务器(Windows Server 2012 R2)。 需要在自定义应用程序(lib spring-security-saml2-core)中添加授权。
身份验证步骤:
1)创建SAML AuthRequest并发送到ADFS服务器
2)用户自动重定向到服务器的登录页面,输入正确的凭据
3)服务器未响应。日志错误:
The Federation Service encountered an error while processing the SAML authentication request.
其他数据 例外详情:
Microsoft.IdentityModel.Protocols.XmlSignature.SignatureVerificationFailedException: ID4037: The key needed to verify the signature could not be resolved from the following security key identifier 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 1,
Clause[0] = Microsoft.IdentityServer.Tokens.MSISSecurityKeyIdentifierClause
)
'. Ensure that the SecurityTokenResolver is populated with the required key.
at Microsoft.IdentityModel.Protocols.XmlSignature.EnvelopedSignatureReader.ResolveSigningCredentials()
at Microsoft.IdentityModel.Protocols.XmlSignature.EnvelopedSignatureReader.OnEndOfRootElement()
at Microsoft.IdentityModel.Protocols.XmlSignature.EnvelopedSignatureReader.Read()
at System.Xml.XmlReader.ReadEndElement()
at Microsoft.IdentityServer.Protocols.Saml.SamlProtocolSerializer.ReadAuthnRequest(XmlReader reader)
at Microsoft.IdentityServer.Protocols.Saml.HttpSamlBindingSerializer.ReadProtocolMessage(String encodedSamlMessage)
at Microsoft.IdentityServer.Protocols.Saml.Contract.SamlContractUtility.CreateSamlMessage(MSISSamlBindingMessage message)
at Microsoft.IdentityServer.Web.Protocols.Saml.SamlProtocolManager.Issue(HttpSamlRequestMessage httpSamlRequestMessage, SecurityTokenElement onBehalfOf, String sessionState, String relayState, String& newSamlSession, String& samlpAuthenticationProvider, Boolean isUrlTranslationNeeded, WrappedHttpListenerContext context, Boolean isKmsiRequested)
有人可以帮助解决此错误吗? 如何获得回应?
我们使用本文来配置证书和jks文件:https://docs.microfocus.com/SM/9.61/Hybrid/Content/security/tasks/configure_saml_setup_relationship_between_idm_adfs.htm
安全XML:
<!-- ADFS filter -->
<bean id="adfsAuthenticationProcessingFilter"
class="com.webapp.filter.ADFSAuthenticationProcessingFilter">
<property name="authenticationManager" ref="samlAuthenticationManager"/>
<property name="authenticationSuccessHandler" ref="logAuthenticationSuccessHandler"/>
<property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
<property name="sessionAuthenticationStrategy" ref="sessionFixationProtectionStrategy"/>
<property name="SAMLProcessor" ref="processor"/>
<property name="contextProvider" ref="contextProvider"/>
<property name="userManager" ref="userManager"/>
</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="http://{ADFS server}/adfs/services/trust"/>
<property name="requestSigned" value="false"/>
<property name="extendedMetadata">
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="signMetadata" value="false"/>
<property name="idpDiscoveryEnabled" value="false"/>
</bean>
</property>
</bean>
</constructor-arg>
<property name="manager" ref="metadata"/>
</bean>
<bean id="metadata" class="org.springframework.security.saml.metadata.MetadataManager">
<constructor-arg>
<list>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<constructor-arg>
<bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
<constructor-arg name="metadata" type="java.io.File"
value="/WEB-INF/saml/sp_metadata.xml"/>
<property name="parserPool" ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="local" value="true"/>
<property name="securityProfile" value="metaiop"/>
<property name="sslSecurityProfile" value="metaiop"/>
<property name="signMetadata" value="true"/>
<property name="signingKey" value="keyName"/>
<property name="encryptionKey" value="keyName"/>
<property name="requireArtifactResolveSigned" value="true"/>
<property name="requireLogoutRequestSigned" value="true"/>
<property name="requireLogoutResponseSigned" value="true"/>
<property name="idpDiscoveryEnabled" value="false"/>
</bean>
</constructor-arg>
<property name="requireValidMetadata" value="false"/>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<constructor-arg>
<bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
<constructor-arg name="metadata" type="java.io.File"
value="/WEB-INF/saml/ip_metadata.xml"/>
<property name="parserPool" ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="local" value="false"/>
<property name="securityProfile" value="metaiop"/>
<property name="sslSecurityProfile" value="metaiop"/>
<property name="signMetadata" value="true"/>
<property name="signingKey" value="keyName"/>
<property name="encryptionKey" value="keyName"/>
<property name="requireArtifactResolveSigned" value="false"/>
<property name="requireLogoutRequestSigned" value="false"/>
<property name="requireLogoutResponseSigned" value="false"/>
<property name="idpDiscoveryEnabled" value="false"/>
</bean>
</constructor-arg>
<property name="requireValidMetadata" value="false"/>
</bean>
</list>
</constructor-arg>
<property name="keyManager" ref="keyManager"/>
<property name="TLSConfigurer">
<bean class="org.springframework.security.saml.trust.httpclient.TLSProtocolConfigurer">
<property name="keyManager" ref="keyManager"/>
<property name="trustedKeys">
<set>
</set>
</property>
</bean>
</property>
<property name="hostedSPName"
value="http://{ADFS server}/adfs/services/trust"/>
<property name="defaultIDP"
value="http://{ADFS server}/adfs/services/trust"/>
</bean>
<bean id="logAuthenticationSuccessHandler" parent="txProxyTemplate">
<property name="target">
<bean class="com.webapp.security.login.LogAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/startPage.htm"/>
<property name="requestCache" ref="savedRequestCache"/>
<property name="userDao" ref="userDao"/>
<property name="attemptsManager" ref="authenticationAttemptsManager"/>
<property name="userManager" ref="userManager"/>
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="onAuthenticationSuccess">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- SAML 2.0 -->
<!-- Secured pages with SAML as entry point -->
<sec:http pattern="/saml/**" entry-point-ref="samlEntryPoint">
<sec:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<sec:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</sec:http>
<sec:http entry-point-ref="samlEntryPoint" use-expressions="false" pattern="/">
<sec:csrf/>
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
<sec:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<sec:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</sec:http>
<!-- Filters for processing of SAML messages -->
<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map request-matcher="ant">
<sec:filter-chain pattern="/saml/login" filters="samlEntryPoint"/>
<sec:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
<sec:filter-chain pattern="/saml/SSO" filters="samlWebSSOProcessingFilter"/>
<sec:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<sec:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
</sec: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="/"/>
</bean>
<!-- Handler deciding where to redirect user after failed login -->
<bean id="failureRedirectHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="useForward" value="true"/>
<property name="defaultFailureUrl" value="/error/403.jsf"/>
</bean>
<!-- Handler for successful logout -->
<bean id="successLogoutHandler"
class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
<property name="defaultTargetUrl" value="/logout"/>
</bean>
<sec:authentication-manager alias="samlAuthenticationManager">
<!-- Register authentication manager for SAML provider -->
<sec:authentication-provider ref="samlAuthenticationProvider"/>
<!-- Register authentication manager for administration UI -->
<sec:authentication-provider>
<sec:user-service id="adminInterfaceService">
<sec:user name="admin" password="admin" authorities="ROLE_ADMIN"/>
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
<!-- Logger for SAML messages and events -->
<bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/>
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="/WEB-INF/saml/samlKeystore.jks"/>
<constructor-arg type="java.lang.String" value="123456"/>
<constructor-arg>
<map>
<entry key="keyName" value="1234567"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="keyName"/>
</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"/>
<property name="nameID"
value="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"/>
<property name="binding" value="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"/>
<property name="allowCreate" value="true"/>
<property name="assertionConsumerIndex" value="0"/>
<property name="passive" value="false"/>
<property name="relayState"
value="https://{application URL}/saml/SSO/login.htm"/>
<property name="forceAuthN" value="true"/>
<property name="authnContexts">
<list>
<value>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
</value>
</list>
</property>
</bean>
</property>
<property name="metadata" ref="metadata"/>
<property name="samlLogger" ref="samlLogger"/>
<property name="webSSOprofile" ref="webSSOprofile"/>
<property name="contextProvider" ref="contextProvider"/>
<property name="filterProcessesUrl" value="/login.htm"/>
</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">
<property name="manager" ref="metadata"/>
<property name="contextProvider" ref="contextProvider"/>
<property name="keyManager" ref="keyManager"/>
</bean>
<!-- SAML Authentication Provider responsible for validating of received SAML messages -->
<bean id="samlAuthenticationProvider"
class="org.springframework.security.saml.SAMLAuthenticationProvider">
<property name="userDetails" ref="userDetails"/>
<property name="consumer" ref="webSSOprofileConsumer"/>
<property name="hokConsumer" ref="hokWebSSOprofileConsumer"/>
<property name="samlLogger" ref="samlLogger"/>
<property name="forcePrincipalAsString" value="false"/>
</bean>
<bean id="userDetails" parent="txProxyTemplate">
<property name="target">
<bean class="com.webapp.security.SAMLUserDetails">
<property name="userDao" ref="userDao"/>
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="load*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- Provider of default SAML Context -->
<bean id="contextProvider"
class="org.springframework.security.saml.context.SAMLContextProviderImpl">
<property name="keyManager" ref="keyManager"/>
<property name="metadata" ref="metadata"/>
</bean>
<!-- 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"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
<property name="contextProvider" ref="contextProvider"/>
<property name="SAMLProcessor" ref="processor"/>
</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"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
<property name="contextProvider" ref="contextProvider"/>
<property name="SAMLProcessor" ref="processor"/>
</bean>
<!-- Logout handler terminating local session -->
<bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
<property name="invalidateHttpSession" value="false"/>
</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"/>
<property name="SAMLProcessor" ref="samlProcessor"/>
<property name="contextProvider" ref="contextProvider"/>
<property name="logoutProfile" ref="logoutProfile"/>
<property name="samlLogger" ref="samlLogger"/>
</bean>
<bean id="logoutProfile"
class="org.springframework.security.saml.websso.SingleLogoutProfileImpl">
<property name="metadata" ref="metadata"/>
<property name="processor" ref="samlProcessor"/>
</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>
<!-- Initialization of OpenSAML library-->
<bean class="org.springframework.security.saml.SAMLBootstrap"/>
<!-- SAML 2.0 WebSSO Assertion Consumer -->
<bean id="webSSOprofileConsumer"
class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl">
<property name="metadata" ref="metadata"/>
<property name="processor" ref="samlProcessor"/>
<property name="responseSkew" value="600"/>
<property name="maxAuthenticationAge" value="7200"/>
</bean>
<!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
<bean id="hokWebSSOprofileConsumer"
class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl">
<property name="metadata" ref="metadata"/>
<property name="processor" ref="samlProcessor"/>
</bean>
<!-- SAML 2.0 Web SSO profile -->
<bean id="webSSOprofile" class="com.webapp.filter.CustomWebSSOProfileImpl">
<property name="metadata" ref="metadata"/>
<property name="processor" ref="samlProcessor"/>
<!-- property name="metadataIssuerUrl" value="https://{ADFS server}/FederationMetadata/2007-06/FederationMetadata.xml" / -->
</bean>
<!-- SAML 2.0 Holder-of-Key Web SSO profile -->
<bean id="hokWebSSOProfile"
class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl">
<property name="metadata" ref="metadata"/>
<property name="processor" ref="samlProcessor"/>
</bean>
<!-- SAML 2.0 ECP profile -->
<bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl">
<property name="metadata" ref="metadata"/>
<property name="processor" ref="samlProcessor"/>
</bean>
<!-- SAML 2.0 Logout Profile -->
<bean id="logoutprofile"
class="org.springframework.security.saml.websso.SingleLogoutProfileImpl">
<property name="metadata" ref="metadata"/>
<property name="processor" ref="samlProcessor"/>
<property name="responseSkew" value="600"/>
</bean>
<!-- 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" ref="samlProcessor"/>
<property name="metadata" ref="metadata"/>
</bean>
</constructor-arg>
</bean>
<bean id="samlProcessor" class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg ref="httpPostBinding"/>
</bean>
<bean id="httpRedirectBinding"
class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="httpPostBinding" class="org.springframework.security.saml.processor.HTTPPostBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
</bean>
<bean id="xmlHttpPostBinding"
class="org.springframework.security.saml.processor.HTTPPostBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg>
<bean class="org.opensaml.saml1.binding.decoding.HTTPPostDecoder">
<constructor-arg ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="com.webapp.filter.HttpRedirectDeflateAsHttpPostEncoder"/>
</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"/>
<bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool"
init-method="initialize"/>
<bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/>
SAML AuthRequest:
<!-- ADFS filter -->
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:AuthnRequest
AssertionConsumerServiceURL="https://{application URL}/saml/SSO/login.htm"
Destination="https://{ADFS server}/adfs/ls/" ForceAuthn="true"
ID="ID" IsPassive="false" IssueInstant="2019-08-28T07:36:16.121Z"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"
xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://{ADFS server URL}/adfs/services/trust</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#ID">
<ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>pA3hIQrobfvJmjpV76SuMhfD5Ig=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>...==</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature><saml2p:NameIDPolicy AllowCreate="true"
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"/>
<saml2p:RequestedAuthnContext Comparison="exact">
<saml2:AuthnContextClassRef xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>
</saml2p:RequestedAuthnContext>
答案 0 :(得分:0)
我最近在一个项目中工作,我们设置了KeyCloak充当ADFS IdP的SP。
只有在设置以下设置后,我们才能使SAML请求正确处理:
IdP URL:${IDP_URL}/adfs/ls/
NameID策略格式:persistent
WantAuthnRequestsSigned:true
WantAssertionsSigned:true
SignatureAlgorithm:RSA_SHA256
SAMLSignatureKeyName:CERT_SUBJECT
除了在我们这一边(作为SP)更新NameID策略外,我们还必须在IdP方面进行自定义设置,以确保将NameID以persistent
格式发送回去。
我从您的身份验证请求中看到,NameID策略当前设置为emailAddress
。