简体   繁体   English

在 Java 中使用 GSSAPI 身份验证时,来自 Active Directory 的搜索结果中的 LDAP Continuation Reference 错误

[英]LDAP Continuation Reference error in search results from Active Directory when using GSSAPI authentication in Java

UPDATE: Based on the comment from @Michael-O below, it seems like the correct way to handle this issue if for the LDAP JNDI provider or the SASL implementation to canonicalize the host name by doing a forward then a revers DNS lookup before issue in KRN service ticket request.更新:根据下面@Michael-O 的评论,如果 LDAP JNDI 提供程序或 SASL 实现通过执行转发然后反向 DNS 查找来规范化主机名,这似乎是处理此问题的正确方法。 KRN 服务票据请求。 I will try to reach out to the Open JDK security list and see if any answers come back from there.我将尝试联系 Open JDK 安全列表,看看是否有任何答案从那里返回。

I am trying to do a recursive LDAP search on the root DN against an Active Directory server using a session that is authenticated via GSSAPI using a Subject from a Kerberos LoginContext.我正在尝试使用通过 GSSAPI 使用 Kerberos LoginContext 中的主题进行身份验证的会话针对 Active Directory 服务器在根 DN 上执行递归 LDAP 搜索。

I am able to successfully bind to the server with URL ldap://dc1.example.com .我能够使用 URL ldap://dc1.example.com成功绑定到服务器。 The InitidalDirContext has java.naming.referral set to follow . InitialDirContextjava.naming.referral设置为follow

When I execute the search (&(objectClass=user)(userPrincipalName=sample_user@EXAMPLE.COM)) against the root DN of dc=example,dc=com , I get one SearchResult back:当我对dc=example,dc=com的根 DN 执行搜索(&(objectClass=user)(userPrincipalName=sample_user@EXAMPLE.COM)) ,我得到一个SearchResult

CN=Sample User,OU=ExampleUsers,DC=example,DC=com

And several Continuation References:和几个继续参考:

ldap://example.com/CN=Configuration,DC=example,DC=com
ldap://ForestDnsZones.example.com/DC=ForestDnsZones,DC=example,DC=com
ldap://DomainDnsZones.example.com/DC=DomainDnsZones,DC=example,DC=com

I can iterate over the SearchResult just fine, but as soon as I encounter a continuation, I get a PartialResultsException .我可以很好地迭代SearchResult ,但是一旦遇到延续,我就会得到PartialResultsException I checked DNS and all of the above host names resolve correctly.我检查了 DNS 并且上述所有主机名都正确解析。 The exception I get looks like this:我得到的异常如下所示:

javax.naming.PartialResultException 
  [Root exception is javax.naming.AuthenticationException: GSSAPI 
    [Root exception is javax.security.sasl.SaslException: GSS initiate failed 
      [Caused by GSSException: No valid credentials provided 
        (Mechanism level: Server not found in Kerberos database (7))]]].

Looking at the Kerberos trace, this error makes sense.查看 Kerberos 跟踪,此错误是有道理的。 When trying to follow the continuation, the LDAP library attempts to bind to ldap://example.com .当尝试遵循延续时,LDAP 库尝试绑定到ldap://example.com Since we're using GSSAPI for authentication, this triggers a service ticket request for ldap/example.com .由于我们使用 GSSAPI 进行身份验证,这会触发ldap/example.com的服务票证请求。 The response I see in the log is:我在日志中看到的响应是:

>>>KRBError:
     sTime is Thu Aug 21 14:27:20 EDT 2014 0000000000000
     suSec is 414575
     error code is 7
     error Message is Server not found in Kerberos database
     realm is EXAMPLE.COM
     sname is ldap/example.com
     msgType is 30

I checked Active Directory, and sure enough there isn't any servicePrincipalName attribute with value ldap/example.com anywhere on any of the domain controllers.我检查了 Active Directory,果然在任何域控制器上的任何地方都没有任何值为ldap/example.com servicePrincipalName属性。 I've tried manually adding a SPN for ldap/example.com to the SAVANT-DC1 domain controller's machine account.我尝试将ldap/example.com的 SPN 手动添加到 SAVANT-DC1 域控制器的计算机帐户。 This works temporarily, but Active Directory seems to automatically purge the SPN entry after a couple of minutes.这暂时有效,但 Active Directory 似乎会在几分钟后自动清除 SPN 条目。

It seems like the solution would be to do one of似乎解决方案是执行以下操作之一

  1. Get Active Directory to return continuations that contain the name of the domain controller instead of the domain.获取 Active Directory 以返回包含域控制器名称而不是域的延续。 We know that we are able to obtain service tickets for SPNs in the form of ldap/dc1.example.com .我们知道我们能够以ldap/dc1.example.com的形式获取 SPN 的服务票证。
  2. Somehow map continuations on the java end of things to redirect ldap://example.com to ldap://dc1.example.com以某种方式映射 java 端的延续以将ldap://example.com重定向到ldap://dc1.example.com

I haven't bee able to figure out how to do (1).我无法弄清楚该怎么做(1)。

I tried doing (2) using the JNDI Manual Referral Handling Example as a guide.我尝试使用JNDI 手册引用处理示例作为指南来做 (2)。 I switched the java.naming.referral property to throw and wrote a custom referral handler that manually overrides the java.naming.provider.url property in the referral context.我将java.naming.referral属性切换为throw并编写了一个自定义引用处理程序,该处理程序手动覆盖引用上下文中的java.naming.provider.url属性。 However LdapReferralException.getReferralContext() seems to ignore the java.naming.provider.url environment property.然而LdapReferralException.getReferralContext()似乎忽略了java.naming.provider.url环境属性。 Looking at the OpenJDK code to LdapReferralContext.java seems to confirm this (line 105).查看LdapReferralContext.java的 OpenJDK 代码似乎证实了这一点(第 105 行)。

So that's where I am: I can't intercept and manipulate the referrals on the Java side because they are treated as a black box by the JNDI API.这就是我所在的位置:我无法拦截和操作 Java 端的引用,因为它们被 JNDI API 视为黑盒。 I can't manually create an LDAP SPN on the AD side of things because it won't stay persistent in the directory.我无法在 AD 端手动创建 LDAP SPN,因为它不会在目录中保持持久性。 Is there anything else I am missing?还有什么我想念的吗?


Here is the code I am running这是我正在运行的代码

import java.io.File;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.ReferralException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;

public class LdapContinuationDemoAction implements PrivilegedExceptionAction<Object> {
  private final String ldapUrl;
  private final String ldapDn;
  private final String username;

  public static void main(String[] argv) {
    try {
      String username = "example_user@EXAMPLE.COM";
      String password = "Password1";
      String ldapUrl  = "ldap://dc1.example.com";
      String searchDn = "dc=example,dc=com";
      String pwd      = System.getProperty("user.dir");
      String krb5Conf = new File(pwd, "krb5.conf").getAbsolutePath();

      System.setProperty("java.security.krb5.conf", krb5Conf);
      System.setProperty("sun.security.krb5.debug", "true");

      // Login to the domain via Kerberos
      LoginContext loginCtx = new LoginContext("doesn't matter", null,
        getUsernamePasswordHandler(username, password),
        getKrb5Configuration());

      System.out.println("********************************");
      System.out.println("      KRB5 Login");
      System.out.println("********************************");
      loginCtx.login();

      // Execute the LDAP search as the user logged in above
      LdapContinuationDemoAction action = new LdapContinuationDemoAction(ldapUrl,
        searchDn, username);

      Subject.doAs(loginCtx.getSubject(), action);
    } catch( Exception e) {
      System.out.println();
      System.out.println("*** ERROR: " + e);
    }
  }

  private LdapContinuationDemoAction(String ldapUrl, String ldapDn,
    String username) {
    this.ldapUrl  = ldapUrl;
    this.ldapDn   = ldapDn;
    this.username = username;
  }

  // Perform a recursive LDAP search for a user principal and print the results
  @Override
  public Object run() throws Exception {
    System.out.println("********************************");
    System.out.println("      LDAP Login");
    System.out.println("********************************");

    //Setup the directory context environment
    Properties dirCtxProps = new Properties();
    dirCtxProps.put(Context.INITIAL_CONTEXT_FACTORY,      "com.sun.jndi.ldap.LdapCtxFactory");
    dirCtxProps.put(Context.PROVIDER_URL,                 this.ldapUrl);
    dirCtxProps.put(Context.SECURITY_AUTHENTICATION,      "GSSAPI");
    dirCtxProps.put("java.naming.ldap.attributes.binary", "objectSID");
    dirCtxProps.put(Context.REFERRAL,                     "follow");

    DirContext dirCtx = new InitialDirContext(dirCtxProps);

    // enable recursive searching
    SearchControls ctrls = new SearchControls();
    ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);

    // do the search
    NamingEnumeration<SearchResult> results = dirCtx.search(this.ldapDn,
      "(&(objectClass=user)(userPrincipalName={0}))",
      new Object[] { this.username }, ctrls);

    System.out.println("********************************");
    System.out.println("      LDAP User Info");
    System.out.println("********************************");
    int resultNum = 0;

    while (results.hasMore()) {
      resultNum++;

      Attributes userAttr = results.next().getAttributes();

      System.out.println("ldap result " + resultNum + ": User DN: "
        + userAttr.get("distinguishedName").get());
      System.out.println();
    }


    return null;
  }

  // JAAS callback handler for username and password Kerberos authn
  private static CallbackHandler getUsernamePasswordHandler(
    final String username, final String password) {

    final CallbackHandler handler = new CallbackHandler() {
      @Override
      public void handle(final Callback[] callback) {
        for (int i = 0; i < callback.length; i++) {
          if (callback[i] instanceof NameCallback) {
            final NameCallback nameCallback = (NameCallback) callback[i];
            nameCallback.setName(username);
          } else if (callback[i] instanceof PasswordCallback) {
            final PasswordCallback passCallback = (PasswordCallback) callback[i];
            passCallback.setPassword(password.toCharArray());
          } else {
            System.err.println("Unsupported Callback: "
              + callback[i].getClass().getName());
          }
        }
      }
    };

    return handler;
  }

  // dynamically build a Kerberos JAAS configuration so we don't need a login.conf
  private static Configuration getKrb5Configuration() {
    return new Configuration() {

      @Override
      public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
        Map<String, String> options = new HashMap<String, String>();
        options.put("client", "true");
        return new AppConfigurationEntry[] {
          new AppConfigurationEntry(
            "com.sun.security.auth.module.Krb5LoginModule",
            LoginModuleControlFlag.REQUIRED, options)
        };
      }
    };
  }

}

Here is my krb5.conf:这是我的 krb5.conf:

[libdefaults]
  default_realm = EXAMPLE.COM

[realms]
  EXAMPLE.COM = {
    kdc = dc1.example.com
    default_domain = example.com
  }

[domain_realm]
  .example.com = EXAMPLE.COM
  example.com = EXAMPLE.COM

Here is the output from the above code这是上面代码的输出

********************************
      KRB5 Login
********************************
Config name: C:\src\scratch\krb5\krb5.conf
>>> KdcAccessibility: reset
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 18 17 16 23 1 3.
>>> KrbAsReq creating message
>>> KrbKdcReq send: kdc=dc1.example.com UDP:88, timeout=30000, number of retries =3, #bytes=158
>>> KDCCommunication: kdc=dc1.example.com UDP:88, timeout=30000,Attempt =1, #bytes=158
>>> KrbKdcReq send: #bytes read=227
>>>Pre-Authentication Data:
     PA-DATA type = 19
     PA-ETYPE-INFO2 etype = 18, salt = EXAMPLE.COMexample_user, s2kparams = null
     PA-ETYPE-INFO2 etype = 23, salt = null, s2kparams = null
     PA-ETYPE-INFO2 etype = 3, salt = EXAMPLE.COMexample_user, s2kparams = null

>>>Pre-Authentication Data:
     PA-DATA type = 2
     PA-ENC-TIMESTAMP
>>>Pre-Authentication Data:
     PA-DATA type = 16

>>>Pre-Authentication Data:
     PA-DATA type = 15

>>> KdcAccessibility: remove dc1.example.com
>>> KDCRep: init() encoding tag is 126 req type is 11
>>>KRBError:
     sTime is Thu Aug 21 16:35:42 EDT 2014 0000000000000
     suSec is 659371
     error code is 25
     error Message is Additional pre-authentication required
     realm is EXAMPLE.COM
     sname is krbtgt/EXAMPLE.COM
     eData provided.
     msgType is 30
>>>Pre-Authentication Data:
     PA-DATA type = 19
     PA-ETYPE-INFO2 etype = 18, salt = EXAMPLE.COMexample_user, s2kparams = null
     PA-ETYPE-INFO2 etype = 23, salt = null, s2kparams = null
     PA-ETYPE-INFO2 etype = 3, salt = EXAMPLE.COMexample_user, s2kparams = null

>>>Pre-Authentication Data:
     PA-DATA type = 2
     PA-ENC-TIMESTAMP
>>>Pre-Authentication Data:
     PA-DATA type = 16

>>>Pre-Authentication Data:
     PA-DATA type = 15

KrbAsReqBuilder: PREAUTH FAILED/REQ, re-send AS-REQ
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 18 17 16 23 1 3.
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 18 17 16 23 1 3.
>>> EType: sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType
>>> KrbAsReq creating message
>>> KrbKdcReq send: kdc=dc1.example.com UDP:88, timeout=30000, number of retries =3, #bytes=240
>>> KDCCommunication: kdc=dc1.example.com UDP:88, timeout=30000,Attempt =1, #bytes=240
>>> KrbKdcReq send: #bytes read=1425
>>> KdcAccessibility: remove dc1.example.com
>>> EType: sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType
>>> KrbAsRep cons in KrbAsReq.getReply example_user
********************************
      LDAP Login
********************************
Found ticket for example_user@EXAMPLE.COM to go to krbtgt/EXAMPLE.COM@EXAMPLE.COM expiring on Fri Aug 22 02:35:42 EDT 2014
Entered Krb5Context.initSecContext with state=STATE_NEW
Found ticket for example_user@EXAMPLE.COM to go to krbtgt/EXAMPLE.COM@EXAMPLE.COM expiring on Fri Aug 22 02:35:42 EDT 2014
Service ticket not found in the subject
>>> Credentials acquireServiceCreds: same realm
Using builtin default etypes for default_tgs_enctypes
default etypes for default_tgs_enctypes: 18 17 16 23 1 3.
>>> CksumType: sun.security.krb5.internal.crypto.RsaMd5CksumType
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>> KrbKdcReq send: kdc=dc1.example.com UDP:88, timeout=30000, number of retries =3, #bytes=1392
>>> KDCCommunication: kdc=dc1.example.com UDP:88, timeout=30000,Attempt =1, #bytes=1392
>>> KrbKdcReq send: #bytes read=1398
>>> KdcAccessibility: remove dc1.example.com
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>> KrbApReq: APOptions are 00000000 00000000 00000000 00000000
>>> EType: sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType
Krb5Context setting mySeqNumber to: 774790609
Krb5Context setting peerSeqNumber to: 0
Created InitSecContextToken:
0000: 01 00 6E 00 00 00 00 00   00 00 A0 03 02 01 05 A1  ..n..)0..%......
0010: 03 02 01 0E A2 00 00 00   00 00 00 00 00 A3 82 04  ................
0020: 00 00 00 00 00 00 00 00   2D A0 03 02 01 05 A1 0E  5a..10..-.......
0030: 1B 0C 55 54 42 53 41 56   2E 4C 4F 43 41 4C A2 2A  ..EXAMPLE.COM.*
0040: 30 28 A0 03 02 01 00 A1   21 30 1F 1B 04 6C 64 61  0(......!0...lda
0050: 70 1B 17 73 61 76 61 6E   74 2D 64 63 31 2E 75 74  p..dc1.ut
0060: 62 73 61 76 2E 6C 6F 63   61 6C A3 82 03 E8 30 82  bsav.local....0.
0070: 03 E4 A0 03 02 01 12 A1   03 02 01 08 A2 82 03 D6  ................
---8<--- Snipping a bunch of binary

Krb5Context.unwrap: token=[05 04 01 ff 00 0c 00 0c 00 00 00 00 2e 2e 5d d1 f5 d2 e8 21 c1 23 92 20 61 f4 77 a8 07 a0 00 00 ]
Krb5Context.unwrap: data=[07 a0 00 00 ]
Krb5Context.wrap: data=[01 01 00 00 ]
Krb5Context.wrap: token=[05 04 00 ff 00 0c 00 00 00 00 00 00 2e 2e 5d d1 00 00 00 00 00 00 00 00 fa b6 79 67 ce db 58 d2 ]
********************************
      LDAP User Info
********************************
ldap result 1: User DN: CN=Sample User,OU=ExampleUsers,DC=example,DC=com

Found ticket for example_user@EXAMPLE.COM to go to krbtgt/EXAMPLE.COM@EXAMPLE.COM expiring on Fri Aug 22 02:35:42 EDT 2014
Entered Krb5Context.initSecContext with state=STATE_NEW
Found ticket for example_user@EXAMPLE.COM to go to krbtgt/EXAMPLE.COM@EXAMPLE.COM expiring on Fri Aug 22 02:35:42 EDT 2014
Found ticket for example_user@EXAMPLE.COM to go to ldap/dc1.example.com@EXAMPLE.COM expiring on Fri Aug 22 02:35:42 EDT 2014
Service ticket not found in the subject
>>> Credentials acquireServiceCreds: same realm
Using builtin default etypes for default_tgs_enctypes
default etypes for default_tgs_enctypes: 18 17 16 23 1 3.
>>> CksumType: sun.security.krb5.internal.crypto.RsaMd5CksumType
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>> KrbKdcReq send: kdc=dc1.example.com UDP:88, timeout=30000, number of retries =3, #bytes=1381
>>> KDCCommunication: kdc=dc1.example.com UDP:88, timeout=30000,Attempt =1, #bytes=1381
>>> KrbKdcReq send: #bytes read=94
>>> KdcAccessibility: remove dc1.example.com
>>> KDCRep: init() encoding tag is 126 req type is 13
>>>KRBError:
     sTime is Thu Aug 21 16:35:46 EDT 2014 0000000000000
     suSec is 918178
     error code is 7
     error Message is Server not found in Kerberos database
     realm is EXAMPLE.COM
     sname is ldap/example.com
     msgType is 30
KrbException: Server not found in Kerberos database (7)
    at sun.security.krb5.KrbTgsRep.<init>(KrbTgsRep.java:70)
    at sun.security.krb5.KrbTgsReq.getReply(KrbTgsReq.java:192)
    at sun.security.krb5.KrbTgsReq.sendAndGetCreds(KrbTgsReq.java:203)
    at sun.security.krb5.internal.CredentialsUtil.serviceCreds(CredentialsUtil.java:311)
    at sun.security.krb5.internal.CredentialsUtil.acquireServiceCreds(CredentialsUtil.java:115)
    at sun.security.krb5.Credentials.acquireServiceCreds(Credentials.java:442)
    at sun.security.jgss.krb5.Krb5Context.initSecContext(Krb5Context.java:641)
    at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:248)
    at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:179)
    at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(GssKrb5Client.java:193)
    at com.sun.jndi.ldap.sasl.LdapSasl.saslBind(LdapSasl.java:123)
    at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:232)
    at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2740)
    at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:316)
    at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:193)
    at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:152)
    at com.sun.jndi.url.ldap.ldapURLContextFactory.getObjectInstance(ldapURLContextFactory.java:52)
    at javax.naming.spi.NamingManager.getURLObject(NamingManager.java:601)
    at javax.naming.spi.NamingManager.processURL(NamingManager.java:381)
    at javax.naming.spi.NamingManager.processURLAddrs(NamingManager.java:361)
    at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:333)
    at com.sun.jndi.ldap.LdapReferralContext.<init>(LdapReferralContext.java:111)
    at com.sun.jndi.ldap.LdapReferralException.getReferralContext(LdapReferralException.java:150)
    at com.sun.jndi.ldap.LdapNamingEnumeration.hasMoreReferrals(LdapNamingEnumeration.java:357)
    at com.sun.jndi.ldap.LdapNamingEnumeration.hasMoreImpl(LdapNamingEnumeration.java:226)
    at com.sun.jndi.ldap.LdapNamingEnumeration.hasMore(LdapNamingEnumeration.java:189)
    at LdapContinuationDemoAction.run(LdapContinuationDemoAction.java:123)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:415)
    at LdapContinuationDemoAction.main(LdapContinuationDemoAction.java:52)
Caused by: KrbException: Identifier doesn't match expected value (906)
    at sun.security.krb5.internal.KDCRep.init(KDCRep.java:143)
    at sun.security.krb5.internal.TGSRep.init(TGSRep.java:66)
    at sun.security.krb5.internal.TGSRep.<init>(TGSRep.java:61)
    at sun.security.krb5.KrbTgsRep.<init>(KrbTgsRep.java:55)
    ... 29 more

*** ERROR: java.security.PrivilegedActionException: javax.naming.PartialResultException [Root exception is javax.naming.AuthenticationException: GSSAPI [Root exception is javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7))]]]

You cannot and shall not register a SPN with the canonical realm name.您不能也不应使用规范领域名称注册 SPN。 SPNs have to be machine specific in this case.在这种情况下,SPN 必须是特定于机器的。 If you really want to use ldap://example.com , make sure that reverse DNS is perfomed before a SPN is built.如果您真的想使用ldap://example.com ,请确保在构建 SPN 之前执行反向 DNS。 MIT Kerberos, Heimdal and JGSS will peform a reverse DNS lookup by default but SSPI won't, so this is not realiable.默认情况下,MIT Kerberos、Heimdal 和 JGSS 将执行反向 DNS 查找,但 SSPI 不会,因此这是不现实的。

A better solution would be rather than providing a hostname, use DNS SRV to locate a DC and then perform a bind.更好的解决方案不是提供主机名,而是使用 DNS SRV 来定位 DC,然后执行绑定。 So change your URL to ldap:///DC=example,DC=com .因此,将您的URL更改为ldap:///DC=example,DC=com

Edit (2016-03-14): After more than 1,5 years I have stumbled upon this myself at work and made some research with Windows tools, Wireshark and Microsoft's documentation on the topic.编辑 (2016-03-14):1,5 年多之后,我自己在工作中偶然发现了这一点,并使用 Windows 工具、Wireshark 和 Microsoft 的有关该主题的文档进行了一些研究。 Some of my previous statements need to be reverted, some updated.我之前的一些陈述需要恢复,一些更新。 Here is the explanation also documented in my Tomcat SPNEGO/AD Authenticator :这是我的Tomcat SPNEGO/AD Authenticator 中也记录的解释:

Edit (2021-06-06): For those who still suffer from this, use my Active Directory DNS Locator and you are done.编辑 (2021-06-06):对于那些仍然受此困扰的人,使用我的Active Directory DNS 定位器即可完成。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Active Directory LDAP 使用 Spring Boot 和 Java 进行身份验证 - Active Directory LDAP Authentication using Spring Boot and Java 无法使用 LDAP 连接从 Java 后端检索 Active Directory 用户 [LDAP:错误代码 32 – 无此类对象] - Unable to retrieve Active Directory users from Java backend using LDAP connection [LDAP: error code 32 – No Such Object] 使用LDAP从Active Directory检索用户属性-JAVA - Retrieving user attributes from Active Directory using LDAP - JAVA 使用LDAP WITHOUT servername从Java(Linux)到Active Directory进行身份验证 - Authenticating from Java (Linux) to Active Directory using LDAP WITHOUT servername 使用Tomcat的Active Directory LDAP身份验证 - Active Directory LDAP Authentication with Tomcat 使用GSSAPI连接到LDAP时出现“客户端和服务器之间没有通用保护层”错误 - “No common protection layer between client and server” error when connecting to LDAP using GSSAPI I got 使用Spring LDAP针对Active Directory进行身份验证的FastBind - FastBind for authentication against Active Directory using Spring LDAP Java使用Apache Directory的LDAP API调用Active Directory - Java calling Active Directory using Apache Directory's LDAP API 如何使用Spring Ldap在Active Directory中对用户进行身份验证和搜索 - How authenticate and search user in Active Directory using Spring Ldap 使用 java 的多个活动目录服务器身份验证 - multiple active directory server authentication using java
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM