具有SAML的Grails 3-身份验证失败

时间:2018-07-13 20:32:40

标签: grails saml spring-saml

我使用以下命令创建了Grails(v3.3.6)网络应用 Grails Spring Security SAML Plugin (v3.3.0)。 不幸的是,我遇到困难 配置正确。这是针对一个名为“同事”的项目的,所以您 将会在各个位置(如下)看到该名称。我们正在使用 PingFederate用于我们的SAML身份验证。

问题:身份验证似乎在PingFederate端起作用,但是 那么浏览器将被发送到无限循环,在我的应用和PingFederate之间来回切换( UPDATE : 已修复,但问题的根源仍然存在)。日志指示MetadataProviderException(请参阅下面的stacktrace的结尾)。

非常感谢您的帮助!

这是我的application.yml配置文件的一部分:

---
grails:
    plugin:
        springsecurity:
            saml:
                active: true  # false does not work as of saml plugin v3.3.0
                afterLoginUrl: '/'
                afterLogoutUrl: '/'
                responseSkew: 300
                signatureAlgorithm: 'rsa-sha256'
                digestAlgorithm: 'sha256'
                userGroupAttribute: 'roles'
                autoCreate:
                    active: false  # If you want the plugin to generate users in the DB as they are authenticated via SAML
                    key: 'id'
                    assignAuthorities: false  # If you want the plugin to assign the authorities that come from the SAML message
                metadata:
                    defaultIdp: 'fed.saml2'  # 'coworkers' # @see https://github.com/jeffwils/grails-spring-security-saml/issues/34#issuecomment-396524285
                    url: '/saml/metadata'
                    providers:
                        ping: 'security/idp.xml'
                    sp:
                        file: 'security/sp.xml'
                        defaults:
                            local: false
                            entityId: 'coworkers'
                            alias: 'coworkers'
                            securityProfile: 'metaiop'  # 'pkix' or 'metaiop'
                            signingKey: 'coworkers'
                            encryptionKey: 'coworkers'
                            tlsKey: 'coworkers'
                            requireArtifactResolveSigned: false
                            requireLogoutRequestSigned: false
                            requireLogoutResponseSigned: false
                keyManager:
                    storeFile: 'classpath:security/mykeystore.jks'
                    storePass: 'mypassword'
                    passwords:
                        coworkers: 'mypassword'
                    defaultKey: ''

这是我的sp.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
  <md:EntityDescriptor entityID="coworkers" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
    <md:SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <md:Extensions>
      <idpdisco:DiscoveryResponse xmlns:idpdisco="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Binding="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Location="http://myserver/login/auth/alias/coworkers?disco=false"/>
    </md:Extensions>
    <md:KeyDescriptor use="signing">
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate>CERTIFICATEREMOVED</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>
    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://myserver/saml/SingleLogout/alias/coworkers"/>
    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://myserver/saml/SingleLogout/alias/coworkers"/>
    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://myserver/saml/SingleLogout/alias/coworkers"/>
    <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
    <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
    <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
    <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
    <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</md:NameIDFormat>
    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://myserver/saml/SSO/alias/coworkers" index="0" isDefault="true"/>
    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="http://myserver/saml/SSO/alias/coworkers" index="1" isDefault="false"/>
    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS" Location="http://myserver/saml/SSO/alias/coworkers" index="2" isDefault="false"/>
  </md:SPSSODescriptor>
</md:EntityDescriptor>

这是我的idp.xml文件:

<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" ID="byhfqDHEyvF6TnZ1-nhQEyP0.kQ" cacheDuration="PT1440M" entityID="fed.saml2">
  <md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" WantAuthnRequestsSigned="true">
    <md:KeyDescriptor use="signing">
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate>CERTIFICATEREMOVED</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>
    <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://fed/idp/SSO.saml2"/>
    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://fed/idp/SSO.saml2"/>
  </md:IDPSSODescriptor>
</md:EntityDescriptor>

这是日志中的堆栈跟踪:

2018-07-13 14:29:00.184 [] DEBUG --- [p-nio-80-exec-3] o.s.security.saml.SAMLProcessingFilter   : Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: Error determining metadata contracts

org.springframework.security.authentication.AuthenticationServiceException: Error determining metadata contracts
        at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:94)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.saml.metadata.MetadataDisplayFilter.doFilter(MetadataDisplayFilter.java:84)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.saml.SAMLEntryPoint.doFilter(SAMLEntryPoint.java:103)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at grails.plugin.springsecurity.web.SecurityRequestHolderFilter.doFilter(SecurityRequestHolderFilter.groovy:58)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:77)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.opensaml.saml2.metadata.provider.MetadataProviderException: No local entity found for alias coworkers, verify your configuration.
        at org.springframework.security.saml.context.SAMLContextProviderImpl.populateLocalEntityId(SAMLContextProviderImpl.java:279)
        at org.springframework.security.saml.context.SAMLContextProviderImpl.getLocalEntity(SAMLContextProviderImpl.java:106)
        at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:79)
        ... 50 common frames omitted

2 个答案:

答案 0 :(得分:2)

我可以看到在application.yml中,您提到的“ entityId”为“ https://coworkers/”,但是在您的SP元数据文件中,entityId为“ http://myserver/coworkers”。使它们相同,然后重试。

答案 1 :(得分:1)

经过大量实验,我终于尝试将saml.metadata.sp.defaults.local设置为true。这似乎已经解决了我的问题!我显然误解了此设置的描述。 @Agam,非常感谢您的协助。