使用Kerberos/Spring Security/IE/Active Directory检测到"检测到令牌"错误(NTLM不是Kerberos)

And*_*Law 30 java kerberos spring-security

我们无法让Spring Security/Kerberos/AD适用于我们的Web应用程序.我们的诊断是我们的AD服务器发送一个NTLM令牌(我们可以告诉它以"TlRMTVNT ....."开头)到IE和IE然后将它发送到我们的应用程序并且它失败了.我们的AD服务器应该向IE发送Kerberos/SPNEGO令牌.

"活动部件"如下:

  • Spring Security 3.0(修补)
  • Microsoft Windows Server Enterprise 2003 SP1 Active Directory
  • IE 8
  • Tomcat(TC Server 6.0)
  • Java 1.6

我们已按照此处的说明详细说明了所有内容:

https://spring.io/blog/2009/09/28/spring-security-kerberos-spnego-extension

这包括:

  • 创建普通用户作为服务主体(与应用程序所在的计算机名称相同).我们设置了以下帐户选项:
    • 已禁用'使用必须在下次登录时更改密码'
    • 已启用'密码永不过期'
    • 启用'使用Kerberos DES ...'
    • 已禁用'不需要Kerberos预身份验证'
    • 注意:Server 2003不显示"此帐户支持Kerberos AES 128位..."和"此帐户支持Kerberos AES 256位..."选项
  • 使用"ktpass.exe"将服务主体名称(SPN)分配给此新用户,并将此用户密钥导出到密钥表文件.使用'ktpass/out ourweb.keytab/mapuser ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK/princ HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK/pass*"
  • https://src.springframework.org/svn/se-security/trunk下载源代码.
  • 将密钥表文件从AD服务器复制到源代码(应用程序)的WEB-INF/etc.
  • 对文件SunJaasKerbersoTicketValidator.java进行了更改以读取keytab文件.(解决应用程序无法从Java类路径读取keytab文件的错误)options.put("keyTab","C:\ se-security\spring-security-kerberos\spring-security-kerberos-sample\SRC \主\ web应用\ WEB-INF \等\ ourweb.keytab");
  • 配置web.xml以使用spnego.xml.contextConfigLocation /WEB-INF/spnego.xml
    • 通过提供我们的服务主页名称和密钥表文件位置,配置Spring Security(spnego.xml)以使用Kerberos(SpnegoEntryPoint,SpnegoAuthenticationProcessingFilter和KerberosServiceAuthenticationProvider bean).
    • 配置spnego.xml以读取在WEB-INF/etc中复制的keytab文件.

当我们启动TC服务器时,我们可以很好地看到事情初始化(即没有错误 - "从keytab获得的原则密钥"):

Creating instance of bean 'org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator#10fa4b8' 
Invoking afterPropertiesSet() on bean with name 'org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator#10fa4b8' 
Config name: C:\WINDOWS\krb5.ini
Debug is  true storeKey true useTicketCache false useKeyTab true doNotPrompt true ticketCache is null isInitiator false KeyTab is C:\se-security\spring-security-kerberos\spring-security-kerberos-sample\src\main\webapp\WEB-INF\etc\ourwebapp4.keytab refreshKrb5Config is false principal is HTTP/ourwebappweb4.testdomain.ourcompany.co.uk tryFirstPass is false useFirstPass is false storePass is false clearPass is false
>>> KeyTabInputStream, readName(): TESTDOMAIN.OURCOMPANY.CO.UK
>>> KeyTabInputStream, readName(): HTTP
>>> KeyTabInputStream, readName(): ourweb
>>> KeyTab: load() entry length: 78; type: 1
>>> KeyTabInputStream, readName(): TESTDOMAIN.OURCOMPANY.CO.UK
>>> KeyTabInputStream, readName(): HTTP
>>> KeyTabInputStream, readName(): ourweb.testdomain.ourcompany.co.uk
>>> KeyTab: load() entry length: 113; type: 1
Added key: 1version: 2
Ordering keys wrt default_tkt_enctypes list
default etypes for default_tkt_enctypes: 1.
0: EncryptionKey: keyType=1 kvno=2 keyValue (hex dump)=
0000: 91 01 43 E3 02 A8 B9 83   

principal's key obtained from the keytab
principal is HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK
EncryptionKey: keyType=1 keyBytes (hex dump)=0000: 91 01 43 E3 02 A8 B9 83   
Added server's keyKerberos Principal HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UKKey Version 2key EncryptionKey: keyType=1 keyBytes (hex dump)=
0000: 91 01 43 E3 02 A8 B9 83   

[Krb5LoginModule] added Krb5Principal  HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK to Subject Commit Succeeded 

Finished creating instance of bean 'org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator#10fa4b8' 
Run Code Online (Sandbox Code Playgroud)

准备测试后,我们在IE中启用了"Windows集成身份验证",并确保该域名已在IE的本地Intranet站点部分中列出.然后,我们使用完全限定的域名连接到我们的Web应用程序.

当我们这样做时,我们在浏览器中遇到以下错误:

500 Internal server error.
Run Code Online (Sandbox Code Playgroud)

并在TC Server日志文件中:

Negotiate Header was invalid: Negotiate     TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAFASgKAAAADw== 
  org.springframework.security.authentication.BadCredentialsException: Kerberos validation not succesfull
  at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:74)
  at org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:92)
  at org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:120)
  at org.springframework.security.authentication.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:48)
  at org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter.doFilter(SpnegoAuthenticationProcessingFilter.java:132)
  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
  at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
  at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:149)
  at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
  at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
  at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
  at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
  at com.springsource.metrics.collection.web.HttpRequestMetricCollectionValve.invoke(HttpRequestMetricCollectionValve.java:44)
  at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
  at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
  at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
  at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
  at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
  at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
  at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:379)
  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:619)
Caused by: java.security.PrivilegedActionException: GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)
  at java.security.AccessController.doPrivileged(Native Method)
  at javax.security.auth.Subject.doAs(Subject.java:396)
  at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:72)
  ... 25 more
Caused by: GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)
  at sun.security.jgss.GSSHeader.<init>(GSSHeader.java:80)
  at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:287)
  at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:267)
  at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:161)
  at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:1)
  ... 28 more
SecurityContextHolder now cleared, as request processing completed
Run Code Online (Sandbox Code Playgroud)

似乎(从我们可以看出)AD服务器发送一个NTLM令牌(我们可以告诉它以"TlRMTVNT ....."开头)到IE和IE然后将它发送到我们的应用程序并且它失败了.

我们的AD服务器应该向IE发送Kerberos/SPNEGO令牌.

其他说明:

  • 我们的服务器(TC服务器)和客户端(浏览器)不同的(虚拟)机在同一个域中.

Gra*_*mak 29

当您在同一台计算机上运行客户端和服务器时,可能会发生这种情况.当您使用IE与运行tomcat的机器通信时,请确保这些是不同的机器.

此外,您需要确保服务器计算机已加入keytab(testdomain.ourcompany.co.uk)中指定的域,或者您可能会退回到NTLM.即使您的服务器位于未加入域的计算机上,您的密钥表仍然可以正常工作(您将看到您显示的好密钥表解密),但IE可能会感到困惑,而不是做正确的事情.

AD只是非常喜欢为Server 2003说arcfour-hmac,所以你需要确保在krb5.ini文件中正确设置它.

您可以像这样正确创建keytab:

C:\>ktpass -princ HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK -mapuser ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK -crypto RC4-HMAC-NT -ptype K
RB5_NT_PRINCIPAL -pass * -out ourweb.keytab
Targeting domain controller: test-dc.ourcompany.co.uk
Using legacy password setting method
Successfully mapped HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK to ourweb.testdomain.ourcompany.co.uk.
Key created.
Output keytab to ourweb.keytab:
Keytab version: 0x502
keysize 75 HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK ptype 1 (KRB5_NT_PRINCIPAL)
vno 3 etype 0x17 (RC4-HMAC) keylength 16 (0x0fd0e500225c4fca9a63a9998b17ca32)
Run Code Online (Sandbox Code Playgroud)

我没有看到你设置了krb5.ini文件.您需要在服务器计算机上正确设置该设置(默认位置C:\ WINDOWS\krb5.ini):

[domain_realm]  
    .testdomain.ourcompany.co.uk = TESTDOMAIN.OURCOMPANY.CO.UK
    testdomain.ourcompany.co.uk = TESTDOMAIN.OURCOMPANY.CO.UK

[libdefaults]   
    default_realm = TESTDOMAIN.OURCOMPANY.CO.UK
    permitted_enctypes = aes128-cts aes256-cts arcfour-hmac-md5 
    default_tgs_enctypes = aes128-cts aes256-cts arcfour-hmac-md5 
    default_tkt_enctypes = aes128-cts aes256-cts arcfour-hmac-md5 

[realms]    
VERDAD.LOCAL = {        
    kdc = test-dc.ourcompany.co.uk  
    admin_server = test-dc.ourcompany.co.uk
    default_domain = TESTDOMAIN.OURCOMPANY.CO.UK
}
Run Code Online (Sandbox Code Playgroud)

您可能还需要设置以下属性(如果您尝试从IDE运行此属性):

<systemProperties>
  <java.security.krb5.kdc>test-dc.ourcompany.co.uk</java.security.krb5.kdc>
  <java.security.krb5.realm>TESTDOMAIN.OURCOMPANY.CO.UK</java.security.krb5.realm>
</systemProperties>
Run Code Online (Sandbox Code Playgroud)

我正在使用maven的org.codehaus.mojo插件,它在pom文件中设置如下:

<build>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>tomcat-maven-plugin</artifactId>
      <configuration>
        <server>tomcat-development-server</server>
        <port>8080</port>
        <path>/SecurityTest</path>
        <systemProperties>
          <java.security.krb5.kdc>test-dc.ourcompany.co.uk</java.security.krb5.kdc
          <java.security.krb5.realm>TESTDOMAIN.OURCOMPANY.CO.UK</java.security.krb5.realm>
        </systemProperties>
      </configuration>
    </plugin>
  </plugins>
</build>
Run Code Online (Sandbox Code Playgroud)

  • 谢谢@Grant.很好的答案.我们检查过,我们在不同的机器上运行客户端和服务器,服务器在域上.但是我们指向我们的keytab.ini文件存在问题.再次感谢.我们还将尝试Maven提示.这是"超越",非常感谢. (3认同)
  • @lilalinux你不能在一台机器上做Kerberos.你需要一个KDC(这些天通常是Microsoft Active Directory),一个客户端(想要一个密钥)和一个服务器(你想要一个密钥).这些都不能全部开启同一台机器,即使是用于测试目的.你需要设置所有这些.这可能会帮助您更好地理解它:https://www.cmf.nrl.navy.mil/krb/kerberos-faq.html#servers (3认同)
  • 我们确实有一个AD,它可以很好地为每个人工作,但只有一个用户.我们发现,他没有登录到他的Windows机器中的域. (3认同)
  • 这是天才.谢谢你的答案格兰特.这几天苦苦挣扎.这应该是设置kerberos的维基. (2认同)
  • 谢谢@MukulGoel我考虑过关于这个的帖子/ wiki,但是认为stackoverflow和任何文件一样好. (2认同)
  • 您可能已经忘记了6年,但是这是否意味着无法在本地测试您是否在Spring应用程序中正确配置了Kerberos安全性? (2认同)
  • Kerberos需要两台机器.您不能仅在本地讲Kerberos. (2认同)
  • 你们有人可以提一下,上面哪些实际上是解决方案?我有同样的麻烦,但只有一台电脑.大多数PC都可以在没有任何登录对话框的情 但无论是否使用domain/realm登录,总会显示一个浏览器对话框然后失败. (2认同)

Lio*_*aga 7

我也遇到过这个问题.对于那些将来会遇到这个问题的不幸的人,这个问题的另一个原因是通过ip访问服务器而不是它的A记录(主机名)


小智 5

我也有同样的问题,花了很长时间才找到罪魁祸首.因此,如果您已完成上述所有操作,仍然使用NTLM令牌而不是kerberos.确保你没有重复的SPN.在我的情况下,我有2个帐户映射到相同的SPN,原因是我以前在同一台服务器上运行一个单独的Web应用程序,使用不同的服务帐户,但映射到相同的SPN,这是HTTP /

希望能帮助到你