Kerberos身份验证通过的Spring Security在IE11和Chrome,但不是失败的Firefox

问题描述:

介绍Kerberos身份验证通过的Spring Security在IE11和Chrome,但不是失败的Firefox

我使用Spring证券Kerberos身份验证处理由网站登录。我遵循指示here并使用here的代码来认证用户。在Firefox中,一切都成功了,下面的登录页面按预期弹出,我可以使用我的Windows登录登录。 enter image description here

但是,在IE和Chrome中验证失败。显示登录屏幕,而不是弹出询问密码的窗口。当我输入Windows用户名和密码时,我会看到下面的屏幕。 enter image description here 尽管HTTP错误500在Chrome和IE上显示,但没有错误显示服务器端来自Spring。

研究

我试图运行示例代码提供here(下弹簧安全Kerberos的样本/秒 - 服务器 - 赢-AUTH),然而同样的问题仍然存在。然而,在这种情况下,春季返回以下错误

org.springframework.security.authentication.BadCredentialsException: Kerberos validation not successful 
    at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:71) 
    at org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:64) 
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156) 
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177) 
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:436) 
    at org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter.doFilter(SpnegoAuthenticationProcessingFilter.java:145) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:85) 
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57) 
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50) 
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) 
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85) 
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) 
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:537) 
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1085) 
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658) 
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222) 
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1556) 
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1513) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
    at java.lang.Thread.run(Thread.java:745) 
Caused by: java.security.PrivilegedActionException: null 
    at java.security.AccessController.doPrivileged(Native Method) 
    at javax.security.auth.Subject.doAs(Subject.java:422) 
    at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:68) 
    ... 45 common frames omitted 
Caused by: org.ietf.jgss.GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag) 
    at sun.security.jgss.GSSHeader.<init>(GSSHeader.java:97) 
    at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:306) 
    at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:285) 
    at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:170) 
    at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:1) 
    ... 48 common frames omitted 

我确定用户名和密码是正确的,但仍然验证失败的IE浏览器和Chrome,但在Firefox成功。

此外,我试着按照教程here这是假设在IE上允许Kerberos身份验证。唯一不同的是不提示密码,但页面上显示500错误。

问题

有我的方式来改变项目的配置在所有浏览器的工作,或者是身份验证的方法目前只打算使用Firefox工作?

关联的文件

的build.gradle

buildscript { 
    ext { 
     springBootVersion = '1.4.1.RELEASE' 
    } 
    repositories { 
     mavenCentral() 
    } 
    dependencies { 
     classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 
    } 
} 

apply plugin: 'java' 
apply plugin: 'eclipse' 
apply plugin: 'spring-boot' 

jar { 
    baseName = 'vlgx-portal-app' 
    version = '0.0.1-SNAPSHOT' 
} 
sourceCompatibility = 1.7 
targetCompatibility = 1.7 

repositories { 
    mavenCentral() 
} 


dependencies { 
    compile('org.springframework.boot:spring-boot-starter-jersey') 
    compile('org.springframework.boot:spring-boot-starter-security') 
    compile('org.springframework.boot:spring-boot-starter-thymeleaf') 
    compile('org.springframework.boot:spring-boot-starter-web') 
    compile('org.springframework.boot:spring-boot-starter-web-services') 
    compile('org.jsoup:jsoup:1.8.1') 
    compile ('javax.mail:mail:1.4.7') 
    compile 'org.springframework.security.kerberos:spring-security-kerberos-web:1.0.1.RELEASE' 
    compile 'org.springframework.security.kerberos:spring-security-kerberos-client:1.0.1.RELEASE' 
    compile 'org.springframework.security:spring-security-ldap:4.2.3.RELEASE' 
    //runtime('org.postgresql:postgresql') 
    testCompile('org.springframework.boot:spring-boot-starter-test') 
} 

WebSecurityConfig.java

package com.valogix.portal.configuration; 

import org.springframework.beans.factory.annotation.Value; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.core.io.FileSystemResource; 
import org.springframework.security.authentication.AuthenticationManager; 
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; 
import org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider; 
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator; 
import org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig; 
import org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource; 
import org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter; 
import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint; 
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider; 
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; 
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper; 
import org.springframework.security.ldap.userdetails.LdapUserDetailsService; 
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; 

@Configuration 
@EnableWebMvcSecurity 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Value("${app.ad-domain}") 
    private String adDomain; 

    @Value("${app.ad-server}") 
    private String adServer; 

    @Value("${app.service-principal}") 
    private String servicePrincipal; 

    @Value("${app.keytab-location}") 
    private String keytabLocation; 

    @Value("${app.ldap-search-base}") 
    private String ldapSearchBase; 

    @Value("${app.ldap-search-filter}") 
    private String ldapSearchFilter; 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http 
      .exceptionHandling() 
       .authenticationEntryPoint(spnegoEntryPoint()) 
       .and() 
      .authorizeRequests() 
       .antMatchers("/login").permitAll() 
       .anyRequest().authenticated() 
       .and() 
      .formLogin() 
       .loginPage("/login").permitAll() 
       .and() 
      .logout() 
       .permitAll() 
       .and() 
      .addFilterBefore(
        spnegoAuthenticationProcessingFilter(authenticationManagerBean()), 
        BasicAuthenticationFilter.class); 
    } 

    @Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
     auth 
      .authenticationProvider(activeDirectoryLdapAuthenticationProvider()) 
      .authenticationProvider(kerberosServiceAuthenticationProvider()); 
    } 

    @Bean 
    public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() { 
     return new ActiveDirectoryLdapAuthenticationProvider(adDomain, adServer); 
    } 

    @Bean 
    public SpnegoEntryPoint spnegoEntryPoint() { 
     return new SpnegoEntryPoint("/login"); 
    } 

    @Bean 
    public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
      AuthenticationManager authenticationManager) { 
     SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter(); 
     filter.setAuthenticationManager(authenticationManager); 
     return filter; 
    } 

    @Bean 
    public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() { 
     KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider(); 
     provider.setTicketValidator(sunJaasKerberosTicketValidator()); 
     provider.setUserDetailsService(ldapUserDetailsService()); 
     return provider; 
    } 

    @Bean 
    public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() { 
     SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator(); 
     ticketValidator.setServicePrincipal(servicePrincipal); 
     ticketValidator.setKeyTabLocation(new FileSystemResource(keytabLocation)); 
     ticketValidator.setDebug(true); 
     return ticketValidator; 
    } 

    @Bean 
    public KerberosLdapContextSource kerberosLdapContextSource() { 
     KerberosLdapContextSource contextSource = new KerberosLdapContextSource(adServer); 
     contextSource.setLoginConfig(loginConfig()); 
     return contextSource; 
    } 

    @Bean 
    public SunJaasKrb5LoginConfig loginConfig() { 
     SunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig(); 
     loginConfig.setKeyTabLocation(new FileSystemResource(keytabLocation)); 
     loginConfig.setServicePrincipal(servicePrincipal); 
     loginConfig.setDebug(true); 
     loginConfig.setIsInitiator(true); 
     return loginConfig; 
    } 

    @Bean 
    public LdapUserDetailsService ldapUserDetailsService() { 
     FilterBasedLdapUserSearch userSearch = 
       new FilterBasedLdapUserSearch(ldapSearchBase, ldapSearchFilter, kerberosLdapContextSource()); 
     LdapUserDetailsService service = new LdapUserDetailsService(userSearch); 
     service.setUserDetailsMapper(new LdapUserDetailsMapper()); 
     return service; 
    } 

} 

application.properties

server.port = 8096 
customerServiceEmail = "[email protected]" 
errorLogDirectory = "error_log_path" 

app.ad-domain: Domain 
app.ad-server: ad_server 
app.service-principal: HTTP/path_or_something 
app.keytab-location: /tmp/tomcat.keytab 
app.ldap-search-base: dc=example,dc=org 
app.ldap-search-filter: "(| (userPrincipalName={0}) (sAMAccountName={0}))" 

如果有什么我忘了请告诉我,并感谢您的时间。

UPDATE

只要我退出弹出的窗口中,Chrome会工作。

+1

[这里](https://ping.force.com/Support/PingFederate/Integrations/How-to-configure-supported-browsers-for-Kerberos-NTLM)是一篇在多个浏览器上解决这个问题的文章[这里](https://productforums.google.com/forum/?hl=zh-CN#!category-topic/chrome/report-a-problem-and-get-troubleshooting-help/lIw5ojBDER4)相应的“Chorme帮助论坛” enrtry。 – xerx593

+0

对不起@ xerx593应该在研究部分提到。我在第一个链接中使用了相同结果的教程(只是不提示密码)和显示的500错误。我现在进行编辑。 –

我设法解决了一点点搞乱的问题。这是在

http 
    .exceptionHandling() 
    .authenticationEntryPoint(spnegoEntryPoint()) 
    .and() 
     .authorizeRequests() 
      .antMatchers("/login").permitAll() 
      .anyRequest().authenticated() 
      .and() 
     .formLogin() 
      .loginPage("/login").permitAll() 
      .and() 
     .logout() 
      .permitAll() 
      .and() 
     .addFilterBefore(
       spnegoAuthenticationProcessingFilter(authenticationManagerBean()), 
       BasicAuthenticationFilter.class); 

的exceptionHandling()引起了弹出窗口显示该要求的用户名和密码,当用户试图连接到他们未认证用户浏览网页。弹出窗口似乎没有正确配置以验证用户身份。我删除了这一点的代码

.exceptionHandling() 
.authenticationEntryPoint(spnegoEntryPoint()) 
.and() 

因为我已经有了一个自定义的登录页面,用户无论如何都被重定向到了。没有更多的错误和用户可以在IE,Chrome和Firefox上登录。

+0

请在此自行接受您自己的答案。 –

它看起来像配置问题(与SPN ...不是Java代码)。我认为Kerberos根本不起作用,但Firefox使用NTLM,这就是它工作的原因。 Chrome通常使用IE中的配置。而IE可能会更严格然后Firefox和不允许坏权证认证:

GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag) 

你可以看一下使用招请求 - 这里是some instruction

仔细检查您的服务帐户和SPN:

setspn.exe -L accountname 

在HTTP协议的情况下,SPN应该有HTTP/machineName.your.domain.com

形式确保您使用的地址http://machineName.your.domain.com来访问你的应用程序。当然,您可以在URL后面添加特定端口(例如8080)或特定路径。但使用与SPN中使用的URL相同的URL - 不要使用IP地址。

我也建议选择性地遵循本指南:SPNEGO SSO using Kerberos

Here也是一些关于微软SPN构建的老话题。

查找是否有任何SPN在其他帐户重复:

setspn.exe -Q <SPN> 

SetSPN command sytax

如在this question中所述,SPN复制会导致NTLM回退。

最后搜索更多关于有缺陷的令牌问题 - here are some good answers