Java 中文官方教程 2022 版(四十七)(3)

简介: Java 中文官方教程 2022 版(四十七)

Java 中文官方教程 2022 版(四十七)(2)https://developer.aliyun.com/article/1488495

结果计数

原文:docs.oracle.com/javase/tutorial/jndi/ops/countlimit.html

有时,查询可能产生太多答案,你希望限制返回的答案数量。你可以通过使用计数限制搜索控件来实现这一点。默认情况下,搜索没有计数限制 - 它将返回它找到的所有答案。要设置搜索的计数限制,请将数字传递给SearchControls.setCountLimit()

以下示例将计数限制设置为 1。

// Set the search controls to limit the count to 1
SearchControls ctls = new SearchControls();
ctls.setCountLimit(1);

如果程序尝试获取超过计数限制数量的结果,那么将抛出SizeLimitExceededException。因此,如果程序设置了计数限制,则应该区分此异常和其他NamingException,或者跟踪计数限制并不请求超过该数量的结果。

指定搜索的计数限制是控制应用程序消耗资源(如内存和网络带宽)的一种方式。控制消耗资源的其他方法包括缩小你的搜索过滤器(更具体地说明你要寻找什么)、在适当的上下文中开始搜索,并使用适当的范围。

时间限制

原文:docs.oracle.com/javase/tutorial/jndi/ops/timelimit.html

对搜索设置时间限制会对搜索操作等待答复的时间上限。当您不希望等待太长时间以获取答复时,这很有用。如果在搜索操作完成之前超过了指定的时间限制,则会抛出TimeLimitExceededException

要设置搜索的时间限制,请将毫秒数传递给SearchControls.setTimeLimit()。以下示例将时间限制设置为 1 秒。

// Set the search controls to limit the time to 1 second (1000 ms)
SearchControls ctls = new SearchControls();
ctls.setTimeLimit(1000);

要使此特定示例超过其时间限制,您需要重新配置它以使用要么是慢的服务器,要么是具有大量条目的服务器。或者,您可以使用其他策略使搜索时间超过 1 秒。

时间限制为零意味着没有设置时间限制,对目录的调用将无限期等待答复。

故障排除提示

原文:docs.oracle.com/javase/tutorial/jndi/ops/faq.html

运行成功编译的使用 JNDI 类的程序时可能遇到的最常见问题如下。


  1. 没有初始上下文
  2. 连接被拒绝
  3. 连接失败
  4. 程序挂起
  5. 找不到名称
  6. 无法连接到任意主机
  7. 无法访问系统属性进行配置
  8. 无法使用 CRAM-MD5 进行身份验证

1. 您收到了NoInitialContextException

原因:您没有指定用于初始上下文的实现。具体来说,Context.INITIAL_CONTEXT_FACTORY 环境属性未设置为将创建初始上下文的工厂类名。或者,您没有使程序可用于服务提供者类(由Context.INITIAL_CONTEXT_FACTORY命名)。

解决方案:将Context.INITIAL_CONTEXT_FACTORY环境属性设置为您正在使用的初始上下文实现的类名。有关详细信息,请参阅配置部分。

如果已设置属性,请确保类名未拼写错误,并且所命名的类对您的程序可用(在其类路径中或安装在 JRE 的 jre/lib/ext 目录中)。Java 平台包括用于 LDAP、COS 命名、DNS 和 RMI 注册表的服务提供者。所有其他服务提供者必须安装并添加到执行环境中。

2. 您收到了CommunicationException,指示“连接被拒绝”。

原因:由Context.PROVIDER_URL环境属性标识的服务器和端口未被服务器提供。也许有人已禁用或关闭了运行服务器的机器。或者,您可能拼写错误了服务器的名称或端口号。

解决方案:检查该端口上确实有服务器在运行,并在必要时重新启动服务器。您执行此检查的方式取决于您使用的 LDAP 服务器。通常,可用管理控制台或工具来管理服务器。您可以使用该工具验证服务器的状态。

3. LDAP 服务器对其他实用程序(如其管理控制台)做出响应,但似乎没有对您程序的请求做出响应。

原因:服务器未对 LDAP v3 连接请求做出响应。一些服务器(特别是公共服务器)未正确响应 LDAP v3,而是忽略请求而不是拒绝它们。此外,一些 LDAP v3 服务器在处理 Oracle 的 LDAP 服务提供者自动发送的控件时存在问题,并经常返回服务器特定的失败代码。

解决方案。尝试将环境属性"java.naming.ldap.version"设置为"2"。LDAP 服务提供程序默认尝试使用 LDAP v3 连接到 LDAP 服务器;如果失败,则使用 LDAP v2。如果服务器悄悄忽略了 v3 请求,则提供程序将假定请求成功。为了解决这样的服务器问题,您必须显式设置协议版本以确保服务器的正确行为。

如果服务器是一个 v3 服务器,那么在创建初始上下文之前尝试设置以下环境属性:

env.put(Context.REFERRAL, "throw");

这将关闭 LDAP 提供程序自动发送的控制。(查看JNDI 教程获取详细信息。)

4. 程序挂起。

原因:一些服务器(特别是公共服务器)在尝试执行会生成太多结果或需要服务器检查太多条目才能生成答案的搜索时,不会响应(甚至不会给出负面答复)。这些服务器试图限制它们在每个请求基础上消耗的资源量。

或者,您尝试使用安全套接字层(SSL)与不支持它的服务器/端口进行通信,反之亦然(也就是说,您尝试使用普通套接字与 SSL 端口进行通信)。

最后,服务器要么由于负载过重而响应非常缓慢,要么由于某种原因根本不响应。

解决方案:如果您的程序因服务器试图限制其资源的使用而挂起,则重试您的请求,使用将返回单个结果或仅返回少量结果的查询。这将帮助您确定服务器是否存活。如果是,则可以扩大您的初始查询并重新提交。

如果您的程序因 SSL 问题而挂起,则需要找出端口是否为 SSL 端口,然后适当设置Context.SECURITY_PROTOCOL环境属性。如果端口是 SSL 端口,则应将此属性设置为"ssl"。如果不是 SSL 端口,则不应设置此属性。

如果您的程序因为以上原因之一而挂起,属性com.sun.jndi.ldap.read.timeout就派上用场了,用于指定读取超时。该属性的值是表示 LDAP 操作的毫秒级读取超时的整数的字符串表示。如果 LDAP 提供程序在该时间段内无法获得 LDAP 响应,则会中止读取尝试。整数应大于零。小于或等于零的整数表示未指定读取超时,相当于一直等待响应直到收到。

如果未指定此属性,则默认是等待响应直到收到为止。

例如,

env.put("com.sun.jndi.ldap.read.timeout", "5000");会导致 LDAP 服务提供程序在服务器在 5 秒内没有回复时中止读取尝试。

5. 您收到NameNotFoundException

原因:当您为 LDAP 初始化初始上下文时,您提供了根专有名称。例如,如果您为初始上下文设置Context.PROVIDER_URL环境属性为"ldap://ldapserver:389/o=JNDITutorial",然后提供了一个名称,如"cn=Joe,c=us",那么您传递给 LDAP 服务的完整名称将是"cn=Joe,c=us,o=JNDITutorial"。如果这确实是您打算的名称,请检查您的服务器,确保它包含这样的条目。

此外,如果您为认证目的提供了不正确的专有名称,Oracle 目录服务器也会返回此错误。例如,如果您将Context.SECURITY_PRINCIPAL环境属性设置为"cn=Admin, o=Tutorial",而"cn=Admin, o=Tutorial"不是 LDAP 服务器上的条目,那么 LDAP 提供程序将抛出NameNotFoundException。实际上,Oracle 目录服务器应返回与认证相关的错误,而不是“未找到名称”。

解决方案:验证您提供的名称是否是服务器上已存在的条目。您可以通过列出条目的父上下文或使用其他工具(如目录服务器的管理控制台)来实现这一点。


在尝试部署使用 JNDI 类的小程序时,您可能会遇到一些问题。

6. 当您的小程序尝试与在与加载小程序的机器不同的机器上运行的目录服务器通信时,您会收到AppletSecurityException

原因:您的小程序未经签名,因此只能连接到加载它的机器。或者,如果小程序已经签名,浏览器尚未授予小程序连接到目录服务器机器的权限。

解决方案:如果您希望允许小程序连接到运行在任意机器上的目录服务器,则需要对您的小程序和小程序将使用的所有 JNDI JAR 文件进行签名。有关签署 jar 文件的信息,请参见签署和验证 JAR 文件。

7. 当您的小程序尝试使用系统属性设置环境属性时,您会收到AppletSecurityException

原因:Web 浏览器限制对系统属性的访问,并且如果您尝试读取它们,则会抛出SecurityException

解决方案:如果您需要为您的小程序获取输入,则尝试使用小程序参数。

8. 当在 Firefox 中运行的小程序尝试使用 CRAM-MD5 对 LDAP 服务器进行身份验证时,您会收到AppletSecurityException

原因: Firefox 禁用对 java.security 包的访问。LDAP 提供程序使用了 java.security.MessageDigest 提供的消息摘要功能来实现 CRAM-MD5。

解决方案: 使用 Java 插件。


课程:LDAP 用户的高级主题

原文:docs.oracle.com/javase/tutorial/jndi/ldap/index.html

LDAP教程中的课程提供了 LDAP 和 JNDI 之间的映射细节。它们还提供了通过 JNDI 访问 LDAP 服务的提示和技巧。

LDAP

X.500 是 CCITT 关于目录服务的标准,是 OSI 服务套件的一部分。X.500 标准定义了一个协议(其中之一)用于客户端应用程序访问 X.500 目录,称为目录访问协议(DAP)。它建立在开放系统互连(OSI)协议栈之上。

因特网社区意识到需要类似 X.500 的服务,但面临不同的底层网络基础设施(TCP/IP 而不是 OSI),设计了一种基于 X.500 DAP 协议的新协议,称为轻量级 DAP,或 LDAP。RFC 2251定义了现在称为版本 3的 LDAP(或 LDAP v3),这是其前身 LDAP v2 的改进版本,其规范在RFC 1777中指定。

LDAP 的目标是设计一种易于实现的协议,特别关注能够构建小型和简单的客户端。它试图通过大量使用字符串和尽可能减少结构的使用来实现简化。例如,DN 在协议中表示为字符串,属性类型名称和许多属性值也是如此。

该协议由客户端向服务器发送请求,服务器做出响应,尽管不一定按照请求发送的顺序。每个请求都带有一个 ID 标记,以便匹配请求和响应。该协议可以在 TCP 或 UDP 上运行,尽管 TCP 版本最常用。

由于对客户端的关注,LDAP 社区还定义了有关 DN 的字符串表示(RFC 2553)、搜索过滤器(RFC 1960)和属性语法(RFC 1778)的标准,以及基于 C 语言的 API(RFC 1823),以及用于访问 LDAP 服务的 URL 格式(RFC 1959)。

LDAP v3 支持国际化、各种认证机制、引荐和通用部署机制。它允许通过使用扩展控制向协议添加新功能,而无需对协议进行更改。

LDAP v3

原文:docs.oracle.com/javase/tutorial/jndi/ldap/ldap.html

国际化

国际化是通过国际字符集(ISO 10646)来处理的,用于表示协议元素是字符串(如 DN)。版本 3 还不同于版本 2,它使用 UTF-8 来编码其字符串。

认证

除了匿名、简单(明文密码)认证外,LDAP v3 还使用简单认证和安全层(SASL)认证框架(RFC 2222)允许使用不同的认证机制与 LDAP 一起使用。SASL 指定了一种挑战-响应协议,其中数据在客户端和服务器之间交换,用于认证目的。

目前定义了几种 SASL 机制:DIGEST-MD5, CRAM-MD5, Anonymous, External, S/Key, GSSAPI, 和 Kerberos v4。LDAP v3 客户端可以使用任何这些 SASL 机制,前提是 LDAP v3 服务器支持它们。此外,可以在不必更改 LDAP 的情况下使用新的(尚未定义的)SASL 机制。

转发

转发是服务器发送回客户端的信息,指示请求的信息可以在另一个位置(可能在另一个服务器上)找到。在 LDAP v2 中,服务器应处理转发而不将其返回给客户端。这是因为处理转发可能非常复杂,因此会导致更复杂的客户端。随着服务器的构建和部署,发现转发很有用,但并不是所有服务器都支持服务器端转发处理。因此,找到了一种方法来改进协议以允许返回转发。这是通过将转发放置在“部分结果”错误响应的错误消息中来完成的。

LDAP v3 明确支持转发,并允许服务器直接将转发返回给客户端。本课程不涵盖转发,但您可以随时参考JNDI 教程来管理应用程序中的转发。

部署

诸如 LDAP 之类的常见协议对确保所有目录客户端和服务器“说同一种语言”非常有用。当在网络中部署许多不同的目录客户端应用程序和目录服务器时,所有这些实体讨论相同的对象也非常有用。

目录模式指定了目录可能具有的对象类型以及每种对象类型可能具有的强制和可选属性,等等。LDAP v3 基于 X.500 标准为网络中常见的对象(如国家、地点、组织、用户/人员、组和设备)定义了一个模式(RFC 2252RFC 2256)。它还定义了客户端应用程序访问服务器模式的方法,以便了解特定服务器支持的对象和属性类型。

LDAP v3 进一步定义了一组用于表示属性值的语法(RFC 2252)。编写需要访问模式的 Java 应用程序,请参考JNDI 教程

扩展

除了预定义的操作集合,如“搜索”和“修改”,LDAP v3 还定义了一个*“扩展”操作*。 “扩展”操作以请求作为参数并返回响应。请求包含标识请求的标识符和请求的参数。响应包含执行请求的结果。 “扩展”操作请求/响应对称为扩展。例如,可以有一个用于启动 TLS 的扩展,这是客户端向服务器发出的激活启动 TLS 协议的请求。

这些扩展可以是标准的(由 LDAP 社区定义)或专有的(由特定目录供应商定义)。编写需要使用扩展的应用程序,请参考JNDI 教程

控制

另一种添加新功能的方法是使用控制。LDAP v3 允许通过使用控制来修改任何操作的行为。可以在操作中发送任意数量的控制,并且可以在其结果中返回任意数量的控制。例如,您可以在“搜索”操作中发送一个排序控制,告诉服务器根据"name"属性对搜索结果进行排序。

像扩展一样,这些控制可以是标准的或专有的。标准控制在平台中提供。编写需要使用控制的应用程序,请参考JNDI 教程

JNDI 作为 LDAP API

原文:docs.oracle.com/javase/tutorial/jndi/ldap/jndi.html

JNDI 和 LDAP 模型都定义了一个层次化的命名空间,您可以在其中命名对象。命名空间中的每个对象都可以具有用于搜索该对象的属性。在这个高层次上,这两个模型是相似的,因此不足为奇 JNDI 很好地映射到 LDAP。

模型

您可以将 LDAP 条目视为 JNDI DirContext。每个 LDAP 条目包含一个名称和一组属性,以及一个可选的子条目集。例如,LDAP 条目"o=JNDITutorial"可能具有其属性"objectclass""o",并且可能具有其子条目"ou=Groups""ou=People"

在 JNDI 中,LDAP 条目"o=JNDITutorial"被表示为一个具有名称"o=JNDITutorial"的上下文,其具有两个子上下文,分别命名为"ou=Groups""ou=People"。LDAP 条目的属性由Attributes接口表示,而单个属性由Attribute接口表示。


请查看本课程的下一部分以了解如何通过 JNDI 访问 LDAP 操作的详细信息。

名称

由于联邦制度的结果,您提供给 JNDI 上下文方法的名称可以跨越多个命名空间。这些被称为复合名称。当使用 JNDI 访问 LDAP 服务时,您应该意识到字符串名称中的斜杠字符(“/”)对 JNDI 具有特殊含义。如果 LDAP 条目的名称包含此字符,则需要对其进行转义(使用反斜杠字符"")。例如,具有名称"cn=O/R"的 LDAP 条目必须呈现为字符串"cn=O\\/R"以供 JNDI 上下文方法使用。有关名称的更多信息,请查看JNDI 教程LdapNameRdn类简化了 LDAP 名称的创建和操作。

在协议中使用的 LDAP 名称始终是完全限定的名称,用于标识从 LDAP 命名空间的根(由服务器定义)开始的条目。以下是一些完全限定的 LDAP 名称的示例。

cn=Ted Geisel, ou=Marketing, o=Some Corporation, c=gb
cn=Vinnie Ryan, ou=People, o=JNDITutorial

在 JNDI 中,然而,名称始终是相对的;也就是说,你总是相对于上下文命名一个对象。例如,你可以将条目"cn=Vinnie Ryan"命名为相对于名为"ou=People, o=JNDITutorial"的上下文。或者你可以将条目"cn=Vinnie Ryan, ou=People"命名为相对于名为"o=JNDITutorial"的上下文。或者,你可以创建一个指向 LDAP 服务器命名空间根的初始上下文,并将条目命名为"cn=Vinnie Ryan, ou=People, o=JNDITutorial"

在 JNDI 中,你还可以使用 LDAP URL 来命名 LDAP 条目。请参阅JNDI 教程中关于 LDAP URL 的讨论。

LDAP 操作如何映射到 JNDI API

原文:docs.oracle.com/javase/tutorial/jndi/ldap/operations.html

LDAP 定义了一组操作或请求(参见 RFC 2251)。在 JNDI 中,这些映射到 DirContextLdapContext 接口上的操作(它们是 Context 的子接口)。例如,当调用者调用 DirContext 方法时,LDAP 服务提供程序通过向 LDAP 服务器发送 LDAP 请求来实现该方法。

下表显示了 LDAP 中的操作如何对应到 JNDI 方法。

LDAP 操作 对应的 JNDI 方法
绑定 JNDI 中创建与 LDAP 服务器的初始连接的对应方式是创建一个 InitialDirContext。当应用程序创建初始上下文时,通过环境属性提供客户端身份验证信息。要更改现有上下文的身份验证信息,请使用 Context.addToEnvironment()Context.removeFromEnvironment()
解绑 Context.close() 用于释放上下文使用的资源。它与 LDAP 的 “unbind” 操作不同之处在于,在给定的服务提供程序实现中,资源可以在上下文之间共享,因此关闭一个上下文不会释放所有资源,如果这些资源正在与另一个上下文共享。如果您的意图是释放所有资源,请确保关闭所有上下文。
搜索 JNDI 中对应的方法是重载 DirContext.search(),接受一个搜索过滤器(RFC 2254)。查看 过滤器 示例。
修改 JNDI 中对应的方法是重载 DirContext.modifyAttributes(),接受一个 DirContext.ModificationItem 数组。查看 修改属性 部分的示例。
add JNDI 中对应的方法是DirContext.bind()DirContext.createSubcontext()。您可以使用其中任一方法来添加新的 LDAP 条目。使用bind(),您不仅可以为新条目指定一组属性,还可以指定要与属性一起添加的 Java 对象。请参阅使用 Attributes 添加、替换绑定部分以获取示例。
delete JNDI 中对应的方法是Context.unbind()Context.destroySubcontext()。您可以使用其中任一方法来删除 LDAP 条目。
modify DN/RDN JNDI 中对应的方法是Context.rename()。请参阅重命名对象部分以获取更多详细信息。
compare JNDI 中对应的操作是一个适当受限的DirContext.search()。请参阅 LDAP 比较部分以获取示例。
abandon 当您关闭一个上下文时,所有未完成的请求都会被放弃。同样,当您关闭一个NamingEnumeration时,相应的 LDAP“搜索”请求也会被放弃。
extended operation JNDI 中对应的方法是LdapContext.extendedOperation()。请参阅JNDI 教程以获取更多详细信息。

LDAP 错误代码与 JNDI 异常的映射方式

原文:docs.oracle.com/javase/tutorial/jndi/ldap/exceptions.html

LDAP 定义了一组状态代码,这些代码是 LDAP 服务器发送的 LDAP 响应中返回的(参见RFC 2251)。在 JNDI 中,错误条件被指示为NamingException的子类的已检查异常。请参阅 Naming Exceptions 部分以获取 JNDI 异常类的概述。

LDAP 服务提供程序将从 LDAP 服务器接收的 LDAP 状态代码转换为适当的NamingException子类。以下表格显示了 LDAP 状态代码与 JNDI 异常之间的映射。

LDAP 状态代码 含义 异常或操作
0 成功 报告成功。
1 操作错误 NamingException
2 协议错误 CommunicationException
3 超出时间限制。 TimeLimitExceededException
4 大小限制超出。 SizeLimitExceededException
5 比较为假。 DirContext.search()使用。不会生成异常。
6 比较为真。 DirContext.search()使用。不会生成异常。
7 不支持的身份验证方法。 AuthenticationNotSupportedException
8 需要强身份验证。 AuthenticationNotSupportedException
9 正在返回部分结果。 如果环境属性"java.naming.referral"设置为"ignore"或错误内容不包含引荐,则抛出PartialResultException。否则,使用内容构建引荐。
10 遇到引荐。 如果环境属性"java.naming.referral"设置为"ignore",则忽略。如果属性设置为"throw",则抛出ReferralException。如果属性设置为"follow",则 LDAP 提供程序处理引荐。如果已超过"java.naming.ldap.referral.limit"属性,则抛出LimitExceededException
11 超出管理限制。 LimitExceededException
12 请求的不可用关键扩展。 OperationNotSupportedException
13 需要机密性。 AuthenticationNotSupportedException
14 SASL 绑定正在进行中。 用于 LDAP 提供程序在认证过程中的内部使用。
16 不存在此属性。 NoSuchAttributeException
17 未定义的属性类型。 InvalidAttributeIdentifierException
18 不匹配。 InvalidSearchFilterException
19 约束违规。 InvalidAttributeValueException
20 属性或值已在使用中。 AttributeInUseException
21 无效的属性语法。 InvalidAttributeValueException
32 不存在此对象。 NameNotFoundException
33 别名问题。 NamingException
34 无效的 DN 语法。 InvalidNameException
35 是一个叶子。 由 LDAP 提供程序使用;通常不会生成异常。
36 别名解引用问题。 NamingException
48 不适当的身份验证。 AuthenticationNotSupportedException
49 无效凭证 AuthenticationException
50 访问权限不足 NoPermissionException
51 忙碌 ServiceUnavailableException
52 不可用 ServiceUnavailableException
53 不愿执行 OperationNotSupportedException
54 检测到循环。 NamingException
64 命名违规 InvalidNameException
65 对象类违规 SchemaViolationException
66 非叶子节点不允许。 ContextNotEmptyException
67 RDN 上不允许。 SchemaViolationException
68 条目已存在。 NameAlreadyBoundException
69 禁止对象类修改。 SchemaViolationException
71 影响多个 DSA。 NamingException
80 其他 NamingException

安全性

原文:docs.oracle.com/javase/tutorial/jndi/ldap/security.html

LDAP 服务提供了一个通用的目录服务。它可以用来存储各种信息。所有 LDAP 服务器都有一套系统来控制谁可以读取和更新目录中的信息。

要访问 LDAP 服务,LDAP 客户端首先必须向服务进行身份验证。也就是说,它必须告诉 LDAP 服务器谁将访问数据,以便服务器可以决定客户端被允许看到和执行什么操作。如果客户端成功向 LDAP 服务器进行身份验证,那么当服务器随后收到来自客户端的请求时,它将检查客户端是否被允许执行该请求。这个过程称为访问控制

LDAP 标准提出了 LDAP 客户端可以向 LDAP 服务器进行身份验证的方式(RFC 2251RFC 2829)。这些内容在 LDAP 身份验证部分和身份验证机制部分中进行了概述。本课程还包含了如何使用匿名、简单和 SASL 身份验证机制的描述。

不同的 LDAP 服务器实现以不同的方式支持访问控制。本课程不讨论这个问题。

LDAP 服务的另一个安全方面是支持使用安全通道与客户端通信,例如发送和接收包含密码和密钥等秘密信息的属性。LDAP 服务器为此目的使用 SSL。本课程还展示了如何与 LDAP 服务提供者一起使用 SSL。

LDAP 的认证方式

原文:docs.oracle.com/javase/tutorial/jndi/ldap/authentication.html

在 LDAP 中,认证信息是在"bind"操作中提供的。在 LDAP v2 中,客户端通过发送包含认证信息的"bind"操作与 LDAP 服务器建立连接。

在 LDAP v3 中,此操作具有相同的目的,但是是可选的。发送 LDAP 请求而不执行"bind"操作的客户端被视为匿名客户端(有关详细信息,请参见匿名部分)。在 LDAP v3 中,"bind"操作可以在连接期间的任何时候发送,可能会多次发送。客户端可以在连接的中间发送"bind"请求以更改其身份。如果请求成功,则所有使用旧身份的连接上的未完成请求都将被丢弃,并且连接将与新身份关联。

在"bind"操作中提供的认证信息取决于客户端选择的认证机制。有关认证机制的讨论,请参见认证机制。

使用 JNDI 进行 LDAP 认证

在 JNDI 中,认证信息是在环境属性中指定的。当您使用InitialDirContext类(或其超类或子类)创建初始上下文时,您提供一组环境属性,其中一些可能包含认证信息。您可以使用以下环境属性来指定认证信息。

  • Context.SECURITY_AUTHENTICATION ("java.naming.security.authentication").
    指定要使用的认证机制。对于 JDK 中的 LDAP 服务提供程序,这可以是以下字符串之一:“none”,“simple”,sasl_mech,其中sasl_mech是一组以空格分隔的 SASL 机制名称。有关这些字符串的描述,请参见认证机制。
  • Context.SECURITY_PRINCIPAL ("java.naming.security.principal").
    指定进行认证的用户/程序的名称,取决于Context.SECURITY_AUTHENTICATION属性的值。有关详细信息和示例,请参见本课程的接下来几节。
  • Context.SECURITY_CREDENTIALS ("java.naming.security.credentials").
    指定进行认证的用户/程序的凭据,取决于Context.SECURITY_AUTHENTICATION属性的值。有关详细信息和示例,请参见本课程的接下来几节。

创建初始上下文时,底层 LDAP 服务提供程序从这些环境属性中提取认证信息,并使用 LDAP 的“绑定”操作将其传递给服务器。

以下示例展示了如何通过使用简单的明文密码,客户端向 LDAP 服务器进行身份验证。

// Set up the environment for creating the initial context
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
// Authenticate as S. User and password "mysecret"
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, 
        "cn=S. User, ou=NewHires, o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, "mysecret");
// Create the initial context
DirContext ctx = new InitialDirContext(env);
// ... do something useful with ctx

为上下文使用不同的认证信息

如果要为现有上下文使用不同的认证信息,则可以使用Context.addToEnvironment()Context.removeFromEnvironment()来更新包含认证信息的环境属性。随后对上下文的方法调用将使用新的认证信息与服务器通信。

以下示例展示了如何在创建上下文后将上下文的认证信息更改为"none"

// Authenticate as S. User and the password "mysecret"
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, 
        "cn=S. User, ou=NewHires, o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, "mysecret");
// Create the initial context
DirContext ctx = new InitialDirContext(env);
// ... do something useful with ctx
// Change to using no authentication
ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "none");
// ... do something useful with ctx

认证失败

认证可能因多种原因而失败。例如,如果提供了不正确的认证信息,比如不正确的密码或主体名称,那么会抛出AuthenticationException

这里是一个变体的示例。这次,不正确的密码导致认证失败。

// Authenticate as S. User and give an incorrect password
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, 
        "cn=S. User, ou=NewHires, o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, "notmysecret");

这将产生以下输出。

javax.naming.AuthenticationException: [LDAP: error code 49 - Invalid Credentials]
        ...

因为不同的服务器支持不同的认证机制,您可能请求服务器不支持的认证机制。在这种情况下,将抛出AuthenticationNotSupportedException

这里是一个变体的示例。这次,不支持的认证机制("custom")导致认证失败。

// Authenticate as S. User and the password "mysecret"
env.put(Context.SECURITY_AUTHENTICATION, "custom");
env.put(Context.SECURITY_PRINCIPAL, 
        "cn=S. User, ou=NewHires, o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, "mysecret");

这将产生以下输出。

javax.naming.AuthenticationNotSupportedException: custom
        ...

身份验证机制

原文:docs.oracle.com/javase/tutorial/jndi/ldap/auth_mechs.html

不同版本的 LDAP 支持不同类型的身份验证。LDAP v2 定义了三种身份验证类型:匿名、简单(明文密码)和 Kerberos v4。

LDAP v3 支持匿名、简单和 SASL 身份验证。SASL 是简单身份验证和安全层(RFC 2222)的缩写。它指定了一种挑战-响应协议,客户端和服务器之间交换数据以进行身份验证,并建立安全层以进行后续通信。通过使用 SASL,LDAP 可以支持 LDAP 客户端和服务器协商的任何类型的身份验证。

本课程包含了如何使用匿名、简单和 SASL 身份验证进行身份验证的描述。

指定身份验证机制

身份验证机制是通过使用Context.SECURITY_AUTHENTICATION环境属性来指定的。该属性可以具有以下值之一。

属性名称 属性值
sasl_mech 一个以空格分隔的 SASL 机制名称列表。使用列出的 SASL 机制之一(例如,"CRAM-MD5"表示使用RFC 2195中描述的 CRAM-MD5 SASL 机制)。
none 不使用身份验证(匿名)
simple 使用弱身份验证(明文密码)

默认机制

如果客户端没有指定任何身份验证环境属性,则默认身份验证机制为"none"。然后客户端将被视为匿名客户端。

如果客户端在不显式指定Context.SECURITY_AUTHENTICATION属性的情况下指定身份验证信息,则默认身份验证机制为"simple"

匿名

原文:docs.oracle.com/javase/tutorial/jndi/ldap/anonymous.html

正如刚才所述,如果没有设置任何身份验证环境属性,那么默认的身份验证机制是"none"。如果客户端将Context.SECURITY_AUTHENTICATION环境属性设置为"none",那么身份验证机制就是"none",所有其他身份验证环境属性都将被忽略。您只有在明确希望忽略可能已设置的任何其他身份验证属性时才需要这样做。无论哪种情况,客户端都将被视为匿名客户端。这意味着服务器不知道也不关心客户端是谁,并且将允许客户端访问(读取和更新)任何已配置为可被任何未经身份验证的客户端访问的数据。

因为 Naming and Directory Operations 课程中的所有目录示例都没有设置任何身份验证环境属性,所以它们都使用匿名身份验证。

这里是一个示例,明确将Context.SECURITY_AUTHENTICATION属性设置为"none"(尽管这样做并不是严格必要的,因为这是默认值)。

// Set up the environment for creating the initial context
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
// Use anonymous authentication
env.put(Context.SECURITY_AUTHENTICATION, "none");
// Create the initial context
DirContext ctx = new InitialDirContext(env);
// ... do something useful with ctx

简单

原文:docs.oracle.com/javase/tutorial/jndi/ldap/simple.html

简单身份验证包括向 LDAP 服务器发送客户端(用户)的完全限定 DN 和客户端的明文密码(参见RFC 2251RFC 2829)。这种机制存在安全问题,因为密码可以从网络中读取。为了避免以这种方式暴露密码,您可以在加密通道(如 SSL)中使用简单身份验证机制,前提是 LDAP 服务器支持。

LDAP v2 和 v3 都支持简单身份验证。

要使用简单身份验证机制,必须设置三个身份验证环境属性如下。

Context.SECURITY_AUTHENTICATION

设置为"simple"

Context.SECURITY_PRINCIPAL

设置为正在进行身份验证的实体的完全限定 DN(例如,"cn=S. User, ou=NewHires, o=JNDITutorial")。它的类型为java.lang.String

Context.SECURITY_CREDENTIALS

设置为主体的密码(例如,"mysecret")。它的类型为java.lang.Stringchar数组(char[])或byte数组(byte[])。如果密码是java.lang.Stringchar数组,则在传输到服务器时使用 UTF-8 进行编码以供 LDAP v3 使用,使用 ISO-Latin-1 供 LDAP v2 使用。如果密码是byte[],则按原样传输到服务器。

查看本节中早期的示例,演示如何使用简单身份验证。


注意: 如果您向Context.SECURITY_CREDENTIALS环境属性提供空字符串、空的byte/char数组或null,则身份验证机制将是"none"。这是因为 LDAP 要求简单身份验证的密码不能为空。如果未提供密码,则协议会自动将身份验证转换为"none"

SASL

原文:docs.oracle.com/javase/tutorial/jndi/ldap/sasl.html

LDAP v3 协议使用SASL来支持可插拔身份验证。这意味着 LDAP 客户端和服务器可以根据客户端和服务器所需的保护级别协商和使用可能是非标准和/或定制的身份验证机制。LDAP v2 协议不支持 SASL。

目前定义了几种 SASL 机制:

LDAP 服务器支持的 SASL 机制

在上述列表中,流行的 LDAP 服务器(如 Oracle、OpenLDAP 和 Microsoft)支持外部、摘要-MD5 和 Kerberos V5。RFC 2829提议将摘要-MD5 用作 LDAP v3 服务器的强制默认机制。

这是一个简单的程序,用于查找 LDAP 服务器支持的 SASL 机制列表。

// Create initial context
DirContext ctx = new InitialDirContext();
// Read supportedSASLMechanisms from root DSE
Attributes attrs = ctx.getAttributes(
    "ldap://localhost:389", new String[]{"supportedSASLMechanisms"});

运行此程序针对支持外部 SASL 机制的服务器产生的输出如下。

{supportedsaslmechanisms=supportedSASLMechanisms: 
                         EXTERNAL, GSSAPI, DIGEST-MD5}

指定身份验证机制

要使用特定的 SASL 机制,您需要在Context.SECURITY_AUTHENTICATION环境属性中指定其 IANA 注册的机制名称。您还可以指定 LDAP 提供程序尝试的机制列表。通过指定一个有序的以空格分隔的机制名称列表来实现。LDAP 提供程序将使用它找到实现的第一个机制。

这是一个示例,要求 LDAP 提供程序尝试获取 DIGEST-MD5 机制的实现,如果不可用,则使用 GSSAPI 的实现。

env.put(Context.SECURITY_AUTHENTICATION, "DIGEST-MD5 GSSAPI");

您可以从应用程序的用户获取此身份验证机制列表。或者您可以通过类似于之前显示的调用询问 LDAP 服务器获取它。LDAP 提供程序本身不会向服务器查询此信息。它只是尝试定位和使用指定机制的实现。

平台中的 LDAP 提供程序内置支持外部、摘要-MD5 和 GSSAPI(Kerberos v5)SASL 机制。您可以添加对其他机制的支持。

指定身份验证机制的输入

一些机制,如 External,不需要额外的输入,仅凭借机制名称就足以进行认证。External 示例展示了如何使用 External SASL 机制。

大多数其他机制需要一些额外的输入。根据机制的不同,输入的类型可能会有所不同。以下是一些机制常见的输入要求。

  • 认证 ID。执行认证的实体的身份。
  • 授权 ID。如果认证成功,应该进行访问控制检查的实体的身份。
  • 认证凭据。例如,密码或密钥。

认证和授权 ID 可能会有所不同,如果程序(如代理服务器)代表另一个实体进行认证。认证 ID 是通过使用Context.SECURITY_PRINCIPAL环境属性指定的。它的类型是java.lang.String

认证 ID 的密码/密钥是通过使用Context.SECURITY_CREDENTIALS环境属性指定的。它的类型是java.lang.Stringchar 数组(char[])或byte 数组(byte[])。如果密码是byte数组,则会使用 UTF-8 编码将其转换为char数组。

如果已设置"java.naming.security.sasl.authorizationId"属性,则其值将用作授权 ID。其值必须是java.lang.String类型。默认情况下,空字符串将用作授权 ID,这将指示服务器从客户端的认证凭据中派生授权 ID。

Digest-MD5 示例展示了如何使用Context.SECURITY_PRINCIPALContext.SECURITY_CREDENTIALS属性进行 Digest-MD5 认证。

如果某个机制需要除了已经描述的之外的输入,那么你需要为该机制定义一个回调对象供其使用,你可以在JNDI 教程中查看回调示例。本课程的下一部分将讨论如何使用 SASL Digest-MD5 认证机制。SASL 策略GSS API(Kerberos v5)CRAM-MD5 机制在 JNDI 教程中有介绍。

Java 中文官方教程 2022 版(四十七)(4)https://developer.aliyun.com/article/1488506

相关文章
|
3天前
|
机器学习/深度学习 移动开发 测试技术
《手把手教你》系列技巧篇(五十一)-java+ selenium自动化测试-字符串操作-下篇(详解教程)
【5月更文挑战第15天】自动化测试中进行断言的时候,我们可能经常遇到的场景。从一个字符串中找出一组数字或者其中的某些关键字,而不是将这一串字符串作为结果进行断言。这个时候就需要我们对字符串进行操作,宏哥这里介绍两种方法:正则和字符串切片函数split()。
18 2
|
10天前
|
Web App开发 JavaScript 前端开发
《手把手教你》系列技巧篇(三十九)-java+ selenium自动化测试-JavaScript的调用执行-上篇(详解教程)
【5月更文挑战第3天】本文介绍了如何在Web自动化测试中使用JavaScript执行器(JavascriptExecutor)来完成Selenium API无法处理的任务。首先,需要将WebDriver转换为JavascriptExecutor对象,然后通过executeScript方法执行JavaScript代码。示例用法包括设置JS代码字符串并调用executeScript。文章提供了两个实战场景:一是当时间插件限制输入时,用JS去除元素的readonly属性;二是处理需滚动才能显示的元素,利用JS滚动页面。还给出了一个滚动到底部的代码示例,并提供了详细步骤和解释。
35 10
|
10天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(三十六)-java+ selenium自动化测试-单选和多选按钮操作-番外篇(详解教程)
【4月更文挑战第28天】本文简要介绍了自动化测试的实战应用,通过一个在线问卷调查(&lt;https://www.sojump.com/m/2792226.aspx/&gt;)为例,展示了如何遍历并点击问卷中的选项。测试思路包括找到单选和多选按钮的共性以定位元素,然后使用for循环进行点击操作。代码设计方面,提供了Java+Selenium的示例代码,通过WebDriver实现自动答题。运行代码后,可以看到控制台输出和浏览器的相应动作。文章最后做了简单的小结,强调了本次实践是对之前单选多选操作的巩固。
26 0
|
1天前
|
前端开发 JavaScript 测试技术
《手把手教你》系列技巧篇(五十三)-java+ selenium自动化测试-上传文件-上篇(详细教程)
【5月更文挑战第17天】本文介绍了在Web自动化测试中处理文件上传操作的方法。Selenium的WebDriver未提供直接的API来处理文件上传,因为这涉及到操作系统级别的窗口交互,而WebDriver无法识别非Web元素。文件上传主要分为两类:基于input控件的上传和非input控件的上传。对于input控件,可以直接使用sendKeys()方法输入文件的绝对路径来模拟选择文件。在项目实战中,给出了一个简单的HTML页面和对应的Java代码示例,展示了如何使用Selenium选取并上传文件。
7 0
|
2天前
|
JavaScript 前端开发 Java
《手把手教你》系列技巧篇(五十二)-java+ selenium自动化测试-处理面包屑(详细教程)
【5月更文挑战第16天】本文介绍了网页面包屑导航的概念、作用及其实现方式。面包屑导航是一种用户界面元素,帮助用户理解他们在网站中的位置并方便返回。通常形式为“首页>分类>子分类>内容页”,最多三层结构。在自动化测试中,处理面包屑涉及获取层级关系和当前层级,可以通过查找包含面包屑的div或ul元素,再提取其中的链接来实现。文中还提供了一个基于HTML和JavaScript的简单示例,并展示了相应的自动化测试代码设计和运行结果。
10 0
|
4天前
|
搜索推荐 Java 测试技术
《手把手教你》系列技巧篇(五十)-java+ selenium自动化测试-字符串操作-上篇(详解教程)
【5月更文挑战第14天】本文介绍了自动化测试中如何从字符串中提取特定信息,主要讲解了两种方法:正则表达式和字符串切片操作。文章提供了一个测试场景,即在搜索引擎中搜索“北京宏哥”并比较百度和必应的搜索结果数量。通过字符串切片函数`split()`,可以从搜索结果的描述中提取出数字。代码示例展示了如何使用Java实现这个功能,包括在百度和必应的搜索页面获取结果数量,并进行比较。文章最后还简单提到了其他字符串截取的方法,如`substring()`和`StringUtils`类中的方法。
19 2
|
5天前
|
JavaScript 前端开发 Java
《手把手教你》系列技巧篇(四十九)-java+ selenium自动化测试-隐藏元素定位与操作(详解教程)
【5月更文挑战第13天】本文主要讨论了在Selenium自动化测试中如何处理前端隐藏元素的问题。隐藏元素通常是通过`type="hidden"`或`style="display: none;"`属性实现的,它们在页面上不可见,但仍然存在于HTML代码中。Selenium可以定位到这些隐藏元素,但无法直接进行点击、输入等操作,会报错“ElementNotInteractableException”。
28 3
|
6天前
|
JavaScript 前端开发 测试技术
《手把手教你》系列技巧篇(四十八)-java+ selenium自动化测试-判断元素是否可操作(详解教程)
【5月更文挑战第12天】本文介绍了WebDriver中用于判断元素状态的三个方法:`isEnabled()`、`isSelected()`和`isDisplayed()`。`isSelected()`检查元素是否被选中,通常用于勾选框。`isDisplayed()`则用来判断元素是否在页面上可见。`isEnabled()`方法确定元素是否可操作,例如是否能点击或输入内容。
18 1
|
7天前
|
存储 JavaScript Java
《手把手教你》系列技巧篇(四十七)-java+ selenium自动化测试-判断元素是否显示(详解教程)
【5月更文挑战第11天】WebDriver 的 `isDisplayed()` 方法用于检查页面元素是否可见,如果元素存在于DOM中且可视,返回`true`,否则返回`false`。在自动化测试中,这个方法常用于验证元素是否真正显示在页面上。示例代码展示了如何使用 `isDisplayed()` 判断百度登录页面的特定错误提示文字是否出现。
18 1
|
8天前
|
JavaScript Java 测试技术
《手把手教你》系列技巧篇(四十六)-java+ selenium自动化测试-web页面定位toast-下篇(详解教程)
【5月更文挑战第10天】本文介绍了使用Java和Selenium进行Web自动化测试的实践,以安居客网站为例。最后,提到了在浏览器开发者工具中调试和观察页面元素的方法。
22 2