【问题标题】:Endless redirect loop in IE when throwing AuthenticationException in Kerberos在 Kerberos 中抛出 AuthenticationException 时,IE 中的无限重定向循环
【发布时间】:2014-10-23 16:49:19
【问题描述】:

在我们的项目中,我们使用 Spring Security 来提供两种身份验证方式。第一个是使用 LDAP 的基于表单的登录,第二个是使用 Kerberos 的 SSO。两种类型共享共同的功能。 Kerberos 基本上为我们提供了主体,然后我们使用它来使用 LDAP 查询 Active Directory 以检索某些数据(部门、电子邮件地址等)。对于基于表单的登录,我们采用用户提供的登录名/密码,使用 LDAP 对照 Active Directory 对其进行验证并检索一些信息。

在这两种方法中,我们都使用(相同的)自定义UserDetailsContextMapper,负责将 LDAP 信息映射到内部用户对象。

在实现的mapUserFromContext 方法中,我们还执行了一些其他验证,例如我们检查连接到传入 LDAP 用户的应用程序内部用户对象是否处于非活动状态。在这种情况下,我们会抛出 AuthenticationException 而不是返回 UserDetails 模型。

如果身份验证失败,应该显示表单登录页面并显示错误消息。

现在的问题是,每当在使用 Internet Explorer 8/9/10 的 Kerberos 环境中引发此类异常时,它都会导致重定向循环。这似乎与我们注入到SpnegoAuthenticationProcessingFilter bean 中的SimpleUrlAuthenticationFailureHandler 有关。 SpnegoAuthenticationProcessingFilter bean 将尝试进行身份验证,我们抛出 AuthenticationException,它将运行到一个 catch 块并通过重定向到表单登录页面来处理失败处理程序。

Firefox 不会进入重定向循环。它显示带有错误消息的登录页面。

此外,在 IE 中使用指向表单登录页面的直接链接将绕过 Kerberos 并正确地重新显示登录页面并显示错误消息,以防我们抛出异常。

Kerberos 和 SSO 工作正常。这只是我们抛出的AuthenticationException 与 Kerberos 的结合导致了问题。

不涉及 HTTP/HTTPS 切换。我们总是在测试环境中使用 HTTP。

问题是:

  • 我们在配置流程时是否犯过任何明显的错误?
  • UserDetailsContextMapper 是执行我们自定义验证的最佳位置吗?我们可以在那里扔AuthenticationException吗?
  • IE 和 Kerberos 是否存在已知的限制,这会阻止它正常工作?

我们的测试环境:

  • 带有 Spring Security Core/LDAP/Web 3.0.7 和 spring-security-kerberos-core-1.0.0.M2 的 Grails 2.2.5 应用程序
  • 用于安全性的 Grails 插件:spring-security-core:1.2.7.3、spring-security-ldap:1.0.6、spring-security-kerberos:0.1
  • Windows 2003 R2 服务器虚拟机(域控制器)
  • Tomcat 7.0.22 与 JDK 1.6.0_27-b07 一起运行(在域控制器上)
  • Kerberos 客户端是使用 Internet Explorer 8/9/10 加入域的 Windows7 虚拟机

整个 Spring Security 相关配置非常广泛,所以我现在将其保持在最低限度,并仅列出我认为对问题很重要的部分。

resources.groovy 中的 Bean 配置:

    ldapUserDetailsMapper(CustomLdapUserDetailsContextMapper) {
        grailsApplication = ref('grailsApplication')
    }

    kerberosUserSearch(FilterBasedLdapUserSearch,
        application.config.grails.plugins.springsecurity.ldap.search.base,
        application.config.grails.plugins.springsecurity.ldap.search.filterKerberos,
        ref('contextSource')) {
    }

    kerberosUserDetailsService(LdapUserDetailsService, ref('kerberosUserSearch'), ref('ldapAuthoritiesPopulator')) {
        userDetailsMapper = ref('ldapUserDetailsMapper')
    }

    kerberosServiceAuthenticationProvider(KerberosServiceAuthenticationProvider) {
        userDetailsService = ref('kerberosUserDetailsService')
        ticketValidator = ref('kerberosTicketValidator')
    }

    authenticationFailureHandler(org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler) {
        defaultFailureUrl = '/login/authfail?login_error=1'
    }

    spnegoAuthenticationProcessingFilter(org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter) {
        authenticationManager = ref('authenticationManager')
        failureHandler = ref('authenticationFailureHandler')
    }

Spring Security 相关配置定义了某些属性(不相关的我省略了):

grails.plugins.springsecurity.ldap.search.searchSubtree = true
grails.plugins.springsecurity.ldap.auth.hideUserNotFoundExceptions = false
grails.plugins.springsecurity.ldap.search.derefLink = true
grails.plugins.springsecurity.ldap.authorities.retrieveGroupRoles = true
grails.plugins.springsecurity.ldap.authorities.retrieveDatabaseRoles = false
grails.plugins.springsecurity.ldap.authorities.ignorePartialResultException = true
grails.plugins.springsecurity.ldap.useRememberMe = false
grails.plugins.springsecurity.providerNames = ['ldapAuthProvider', 'kerberosServiceAuthenticationProvider']
grails.plugins.springsecurity.securityConfigType = "InterceptUrlMap"
grails.plugins.springsecurity.interceptUrlMap = [
                '/login/**':     ['IS_AUTHENTICATED_ANONYMOUSLY'],
                '/logout/**':    ['IS_AUTHENTICATED_ANONYMOUSLY'],
                '/**':           ['IS_AUTHENTICATED_FULLY']
]

使用 Fiddler 可以观察到的请求有:

  • 使用 401 响应(WWW-Authenticate: Negotiate)向应用程序根请求(我在浏览器中打开应用程序)。
  • 对应用程序根的请求(授权:协商 ....),带有位置标头的 302 响应到 authfail 页面和一个以 b9 开头的奇怪正文,然后是带有“重定向到...”链接的 HTML 标记和它以 0 结束。这个 302 响应与我们抛出的 AuthenticationException 相关。
  • 请求失败 URL /app/login/authfail?login_error=1 和 302 响应(循环从这里开始并重复)

如果需要,我可以提供更多详细信息。非常感谢任何帮助和 cmets。

Spring 安全调试日志记录:

2014-09-01 08:37:54,884 [http-apr-8080-exec-4] DEBUG web.FilterChainProxy  - Converted URL to lowercase, from: '/index.gsp'; to: '/index.gsp'
2014-09-01 08:37:54,884 [http-apr-8080-exec-4] DEBUG web.FilterChainProxy  - Candidate is: '/index.gsp'; pattern is /**; matched=true
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG web.FilterChainProxy  - /index.gsp at position 1 of 9 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG context.HttpSessionSecurityContextRepository  - No HttpSession currently exists
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG context.HttpSessionSecurityContextRepository  - No SecurityContext was available from the HttpSession: null. A new one will be created.
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG web.FilterChainProxy  - /index.gsp at position 2 of 9 in additional filter chain; firing Filter: 'MutableLogoutFilter'
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG web.FilterChainProxy  - /index.gsp at position 3 of 9 in additional filter chain; firing Filter: 'RequestHolderAuthenticationFilter'
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG web.FilterChainProxy  - /index.gsp at position 4 of 9 in additional filter chain; firing Filter: 'SpnegoAuthenticationProcessingFilter'
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG web.FilterChainProxy  - /index.gsp at position 5 of 9 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG web.FilterChainProxy  - /index.gsp at position 6 of 9 in additional filter chain; firing Filter: 'RememberMeAuthenticationFilter'
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG web.FilterChainProxy  - /index.gsp at position 7 of 9 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG authentication.AnonymousAuthenticationFilter  - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@6faa3d44: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff4c9c: RemoteIpAddress: 192.168.0.126; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG web.FilterChainProxy  - /index.gsp at position 8 of 9 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG web.FilterChainProxy  - /index.gsp at position 9 of 9 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG intercept.FilterSecurityInterceptor  - Secure object: FilterInvocation: URL: /index.gsp; Attributes: [IS_AUTHENTICATED_FULLY]
2014-09-01 08:37:54,900 [http-apr-8080-exec-4] DEBUG intercept.FilterSecurityInterceptor  - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@6faa3d44: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff4c9c: RemoteIpAddress: 192.168.0.126; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2014-09-01 08:37:54,931 [http-apr-8080-exec-4] DEBUG access.ExceptionTranslationFilter  - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
2014-09-01 08:37:54,931 [http-apr-8080-exec-4] DEBUG savedrequest.HttpSessionRequestCache  - DefaultSavedRequest added to Session: DefaultSavedRequest[http://vs-vm-dev2003:8080/app/]
2014-09-01 08:37:54,931 [http-apr-8080-exec-4] DEBUG access.ExceptionTranslationFilter  - Calling Authentication entry point.
2014-09-01 08:37:54,931 [http-apr-8080-exec-4] DEBUG web.SpnegoEntryPoint  - Sending back Negotiate Header for request: http://vs-vm-dev2003:8080/app/
2014-09-01 08:37:54,931 [http-apr-8080-exec-4] DEBUG context.HttpSessionSecurityContextRepository  - SecurityContext is empty or anonymous - context will not be stored in HttpSession. 
2014-09-01 08:37:54,931 [http-apr-8080-exec-4] DEBUG context.SecurityContextPersistenceFilter  - SecurityContextHolder now cleared, as request processing completed
2014-09-01 08:37:54,993 [http-apr-8080-exec-5] DEBUG web.FilterChainProxy  - Converted URL to lowercase, from: '/index.gsp'; to: '/index.gsp'
2014-09-01 08:37:54,993 [http-apr-8080-exec-5] DEBUG web.FilterChainProxy  - Candidate is: '/index.gsp'; pattern is /**; matched=true
2014-09-01 08:37:54,993 [http-apr-8080-exec-5] DEBUG web.FilterChainProxy  - /index.gsp at position 1 of 9 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2014-09-01 08:37:54,993 [http-apr-8080-exec-5] DEBUG context.HttpSessionSecurityContextRepository  - HttpSession returned null object for SPRING_SECURITY_CONTEXT
2014-09-01 08:37:54,993 [http-apr-8080-exec-5] DEBUG context.HttpSessionSecurityContextRepository  - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@18a92eb. A new one will be created.
2014-09-01 08:37:54,993 [http-apr-8080-exec-5] DEBUG web.FilterChainProxy  - /index.gsp at position 2 of 9 in additional filter chain; firing Filter: 'MutableLogoutFilter'
2014-09-01 08:37:54,993 [http-apr-8080-exec-5] DEBUG web.FilterChainProxy  - /index.gsp at position 3 of 9 in additional filter chain; firing Filter: 'RequestHolderAuthenticationFilter'
2014-09-01 08:37:54,993 [http-apr-8080-exec-5] DEBUG web.FilterChainProxy  - /index.gsp at position 4 of 9 in additional filter chain; firing Filter: 'SpnegoAuthenticationProcessingFilter'
2014-09-01 08:37:54,993 [http-apr-8080-exec-5] DEBUG web.SpnegoAuthenticationProcessingFilter  - Received Negotiate Header for request http://vs-vm-dev2003:8080/app/: Negotiate <omitted>
2014-09-01 08:37:55,009 [http-apr-8080-exec-5] DEBUG authentication.ProviderManager  - Authentication attempt using org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider
2014-09-01 08:37:55,009 [http-apr-8080-exec-5] DEBUG kerberos.KerberosServiceAuthenticationProvider  - Try to validate Kerberos Token
Found key for HTTP/vs-vm-dev2003@DOMAIN.LOCAL(23)
Entered Krb5Context.acceptSecContext with state=STATE_NEW
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Using builtin default etypes for permitted_enctypes
default etypes for permitted_enctypes: 3 1 23 16 17 18.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
replay cache for TESTER@DOMAIN.LOCAL is null.
object 0: 1409575075000/30
object 0: 1409575075000/30
>>> KrbApReq: authenticate succeed.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>>Delegated Creds have pname=TESTER@DOMAIN.LOCAL sname=krbtgt/DOMAIN.LOCAL@DOMAIN.LOCAL authtime=null starttime=20140901123744Z endtime=20140901223743ZrenewTill=20140908123743Z
Krb5Context setting peerSeqNumber to: 207387731
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Krb5Context setting mySeqNumber to: 159224545
2014-09-01 08:37:55,212 [http-apr-8080-exec-5] DEBUG kerberos.KerberosServiceAuthenticationProvider  - Succesfully validated TESTER@DOMAIN.LOCAL
2014-09-01 08:37:55,212 [http-apr-8080-exec-5] DEBUG search.FilterBasedLdapUserSearch  - Searching for user 'TESTER@DOMAIN.LOCAL', with user search [ searchFilter: 'mail={0}', searchBase: 'DC=company,DC=com', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2014-09-01 08:37:55,603 [http-apr-8080-exec-5] DEBUG ldap.SpringSecurityLdapTemplate  - Searching for entry under DN '', base = 'dc=company,dc=com', filter = 'mail={0}'
2014-09-01 08:37:55,618 [http-apr-8080-exec-5] DEBUG ldap.SpringSecurityLdapTemplate  - Found DN: cn=TESTER,ou=Users,ou=Users,dc=company,dc=com
2014-09-01 08:37:55,634 [http-apr-8080-exec-5] DEBUG userdetails.DefaultLdapAuthoritiesPopulator  - Getting authorities for user cn=TESTER,ou=Users,ou=Users,dc=company,dc=com
2014-09-01 08:37:55,634 [http-apr-8080-exec-5] DEBUG userdetails.DefaultLdapAuthoritiesPopulator  - Searching for roles for user 'TESTER@DOMAIN.LOCAL', DN = 'cn=TESTER,ou=Users,ou=Users,dc=company,dc=com', with filter member={0} in search base 'OU=Groups,OU=Users,DC=company,DC=com'
2014-09-01 08:37:55,634 [http-apr-8080-exec-5] DEBUG ldap.SpringSecurityLdapTemplate  - Using filter: member=cn=TESTER,ou=Users,ou=Users,dc=company,dc=com
2014-09-01 08:37:55,665 [http-apr-8080-exec-5] DEBUG userdetails.DefaultLdapAuthoritiesPopulator  - Roles from search: []
2014-09-01 08:37:55,665 [http-apr-8080-exec-5] DEBUG security.CustomLdapUserDetailsContextMapper  - Mapping user details from context with DN: cn=TESTER,ou=Users,ou=Users,dc=company,dc=com
2014-09-01 08:37:56,103 [http-apr-8080-exec-5] ERROR security.CustomLdapUserDetailsContextMapper  - The user TESTER@DOMAIN.LOCAL does not have a corresponding SysX login configured.
2014-09-01 08:37:56,571 [http-apr-8080-exec-5] WARN  web.SpnegoAuthenticationProcessingFilter  - Negotiate Header was invalid: Negotiate <omitted>
com.company.app.security.ex.NoCorrespondingSysXLoginAuthenticationException: 
    at com.company.app.security.CustomLdapUserDetailsContextMapper$_mapUserFromContext_closure5.doCall(CustomLdapUserDetailsContextMapper.groovy:73)
    at org.grails.datastore.gorm.GormStaticApi.withTransaction(GormStaticApi.groovy:687)
    at com.company.app.security.CustomLdapUserDetailsContextMapper.mapUserFromContext(CustomLdapUserDetailsContextMapper.groovy:53)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
2014-09-01 08:37:56,571 [http-apr-8080-exec-5] DEBUG authentication.SimpleUrlAuthenticationFailureHandler  - Redirecting to /login/authfail?login_error=1
2014-09-01 08:37:56,571 [http-apr-8080-exec-5] DEBUG web.DefaultRedirectStrategy  - Redirecting to '/app/login/authfail?login_error=1'
2014-09-01 08:37:56,571 [http-apr-8080-exec-5] DEBUG context.HttpSessionSecurityContextRepository  - SecurityContext is empty or anonymous - context will not be stored in HttpSession. 
2014-09-01 08:37:56,571 [http-apr-8080-exec-5] DEBUG context.SecurityContextPersistenceFilter  - SecurityContextHolder now cleared, as request processing completed
2014-09-01 08:37:56,571 [http-apr-8080-exec-6] DEBUG web.FilterChainProxy  - Converted URL to lowercase, from: '/login/authfail'; to: '/login/authfail'
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG web.FilterChainProxy  - Candidate is: '/login/authfail'; pattern is /**; matched=true
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG web.FilterChainProxy  - /login/authfail?login_error=1 at position 1 of 9 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG context.HttpSessionSecurityContextRepository  - HttpSession returned null object for SPRING_SECURITY_CONTEXT
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG context.HttpSessionSecurityContextRepository  - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@18a92eb. A new one will be created.
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG web.FilterChainProxy  - /login/authfail?login_error=1 at position 2 of 9 in additional filter chain; firing Filter: 'MutableLogoutFilter'
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG web.FilterChainProxy  - /login/authfail?login_error=1 at position 3 of 9 in additional filter chain; firing Filter: 'RequestHolderAuthenticationFilter'
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG web.FilterChainProxy  - /login/authfail?login_error=1 at position 4 of 9 in additional filter chain; firing Filter: 'SpnegoAuthenticationProcessingFilter'
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG web.SpnegoAuthenticationProcessingFilter  - Received Negotiate Header for request http://vs-vm-dev2003:8080/app/login/authfail: Negotiate <omitted>
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG authentication.ProviderManager  - Authentication attempt using org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG kerberos.KerberosServiceAuthenticationProvider  - Try to validate Kerberos Token
Found key for HTTP/vs-vm-dev2003@DOMAIN.LOCAL(23)
Entered Krb5Context.acceptSecContext with state=STATE_NEW
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Using builtin default etypes for permitted_enctypes
default etypes for permitted_enctypes: 3 1 23 16 17 18.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
object 0: 1409575076000/31
object 1: 1409575075000/30
object 0: 1409575076000/31
object 1: 1409575075000/30
replay cache found.
>>> KrbApReq: authenticate succeed.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>>Delegated Creds have pname=TESTER@DOMAIN.LOCAL sname=krbtgt/DOMAIN.LOCAL@DOMAIN.LOCAL authtime=null starttime=20140901123744Z endtime=20140901223743ZrenewTill=20140908123743Z
Krb5Context setting peerSeqNumber to: 255994332
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Krb5Context setting mySeqNumber to: 867958603
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG kerberos.KerberosServiceAuthenticationProvider  - Succesfully validated TESTER@DOMAIN.LOCAL
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG search.FilterBasedLdapUserSearch  - Searching for user 'TESTER@DOMAIN.LOCAL', with user search [ searchFilter: 'mail={0}', searchBase: 'DC=company,DC=com', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG ldap.SpringSecurityLdapTemplate  - Searching for entry under DN '', base = 'dc=company,dc=com', filter = 'mail={0}'
2014-09-01 08:37:56,587 [http-apr-8080-exec-6] DEBUG ldap.SpringSecurityLdapTemplate  - Found DN: cn=TESTER,ou=Users,ou=Users,dc=company,dc=com
2014-09-01 08:37:56,603 [http-apr-8080-exec-6] DEBUG userdetails.DefaultLdapAuthoritiesPopulator  - Getting authorities for user cn=TESTER,ou=Users,ou=Users,dc=company,dc=com
2014-09-01 08:37:56,603 [http-apr-8080-exec-6] DEBUG userdetails.DefaultLdapAuthoritiesPopulator  - Searching for roles for user 'TESTER@DOMAIN.LOCAL', DN = 'cn=TESTER,ou=Users,ou=Users,dc=company,dc=com', with filter member={0} in search base 'OU=Groups,OU=Users,DC=company,DC=com'
2014-09-01 08:37:56,603 [http-apr-8080-exec-6] DEBUG ldap.SpringSecurityLdapTemplate  - Using filter: member=cn=TESTER,ou=Users,ou=Users,dc=company,dc=com
2014-09-01 08:37:56,618 [http-apr-8080-exec-6] DEBUG userdetails.DefaultLdapAuthoritiesPopulator  - Roles from search: []
2014-09-01 08:37:56,618 [http-apr-8080-exec-6] DEBUG security.CustomLdapUserDetailsContextMapper  - Mapping user details from context with DN: cn=TESTER,ou=Users,ou=Users,dc=company,dc=com
2014-09-01 08:37:56,790 [http-apr-8080-exec-6] ERROR security.CustomLdapUserDetailsContextMapper  - The user TESTER@DOMAIN.LOCAL does not have a corresponding SysX login configured.
2014-09-01 08:37:56,868 [http-apr-8080-exec-6] WARN  web.SpnegoAuthenticationProcessingFilter  - Negotiate Header was invalid: Negotiate <omitted>
com.company.app.security.ex.NoCorrespondingSysXLoginAuthenticationException: 
    at com.company.app.security.CustomLdapUserDetailsContextMapper$_mapUserFromContext_closure5.doCall(CustomLdapUserDetailsContextMapper.groovy:73)
    at org.grails.datastore.gorm.GormStaticApi.withTransaction(GormStaticApi.groovy:687)
    at com.company.app.security.CustomLdapUserDetailsContextMapper.mapUserFromContext(CustomLdapUserDetailsContextMapper.groovy:53)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
2014-09-01 08:37:56,868 [http-apr-8080-exec-6] DEBUG authentication.SimpleUrlAuthenticationFailureHandler  - Redirecting to /login/authfail?login_error=1
2014-09-01 08:37:56,868 [http-apr-8080-exec-6] DEBUG web.DefaultRedirectStrategy  - Redirecting to '/app/login/authfail?login_error=1'
2014-09-01 08:37:56,868 [http-apr-8080-exec-6] DEBUG context.HttpSessionSecurityContextRepository  - SecurityContext is empty or anonymous - context will not be stored in HttpSession. 
2014-09-01 08:37:56,868 [http-apr-8080-exec-6] DEBUG context.SecurityContextPersistenceFilter  - SecurityContextHolder now cleared, as request processing completed

【问题讨论】:

  • authfail 动作在控制器中有什么作用?它是否尝试重定向到另一个 URL?
  • @rmlan 事实上,确实如此。 authfail 从会话中提取异常并创建错误消息。它将错误消息放入闪存并重定向到同一控制器中的身份验证操作。似乎是标准的 Grails 方式。我们向它添加了一些调试日志记录,似乎这些方法从未真正在重定向循环中调用。
  • 你是否开启了 Spring Security 调试日志?这将向您显示它匹配的各种 URL 以及它正在运行的过滤器。这可能会为您提供有关用户被重定向到受保护页面的原因的线索。这里的关键是确保用户拥有您希望他/她拥有的匿名令牌。
  • 这个问题与 LDAP 或 Kerberos 没有任何关系。只是导航配置错误。
  • @rmlan 我已将调试日志添加到问题中(稍作修改以保护无辜者)。感谢您对此进行调查。如果您发现任何问题,请告诉我。

标签: grails spring-security


【解决方案1】:

当抛出身份验证异常时,由于此配置,它会导致重定向:

authenticationFailureHandler(org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler) {
    defaultFailureUrl = '/login/authfail?login_error=1'
}

由于某种原因,IE 会在每个请求中不断发送 Negotiate 标头,从而触发 SpnegoAuthenticationProcessingFilter 中的处理,进而尝试使用引发身份验证异常的自定义 UserDetailsContextMapper 加载用户详细信息。这会导致无限的重定向循环。

一种解决方法是绕过登录/注销页面的SpnegoAuthenticationProcessingFilter,以便忽略Negotiate 标头,尽管存在。

以下 Spring Security 配置可以解决问题:

grails.plugins.springsecurity.filterChain.chainMap = [
    '/login/**': 'JOINED_FILTERS,-spnegoAuthenticationProcessingFilter',
    '/logout/**': 'JOINED_FILTERS,-spnegoAuthenticationProcessingFilter',
    '/**': 'JOINED_FILTERS'
]

【讨论】:

    猜你喜欢
    • 2021-10-13
    • 1970-01-01
    • 1970-01-01
    • 2016-03-14
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    • 2014-03-18
    相关资源
    最近更新 更多