3.4. 主题(Subject)验证
主题验证要求 JAAS 登录。登录过程由下列要点组成:
- 如
LoginModule
配置所要求的,用程序实例化一个LoginContext
并传入登录配置的名称和CallbackHandler
来填充Callback
对象。 LoginContext
咨询Configuration
来加载包括在命名登录配置里的所有LoginModules
。如果不存在这个名称的配置,那么默认会使用other
配置。- 应用程序调用
LoginContext.login
方法。 - 登录模块调用所有加载的
LoginModule
。每个LoginModule
都会试图验证这个主题,它调用相关联的CallbackHandler
上的 handle 犯法来获取验证过程所需的信息。这些信息以Callback
对象队列的方式被传入 handle 方法。成功后,LoginModule
将主题和相关的主体(principal)和凭证关联。 LoginContext
返回验证状态给应用程序。从 login 方法返回则表示成功。如果 login 方法抛出异常则表示失败。- 如果验证成功,应用程序使用
LoginContext.getSubject
方法获取已验证的主题。 - 在主题验证的作用域完成后,所有的主体以及和
login
方法的主题相关的信息都可以通过调用LoginContext.logout
方法进行删除。
LoginContext
类提供了用于验证主题的基本方法以及开发独立于底层验证技术的应用程序的途径。LoginContext
咨询 Configuration
来决定为特定应用程序配置的验证服务。LoginModule
类代表验证服务。因此,你可以插入不同的等录模块到应用程序而无需修改应用程序自身。下面的步骤显示了应用程序验证主题所需的步骤。
开发者通过创建
LoginModule
接口的实现集成验证技术。这允许管理员插入不同的验证技术到应用程序里。你可以将多个 LoginModule
链接在一起来允许多种验证技术来参与验证过程。例如,一个 LoginModule
可以执行基于用户/密码的验证,而另外一个则可以连接硬件设备如智能卡读写器或生物特征识别器。
LoginModule
的生命周期是由客户创建和发行 login 方法所根据的 LoginContext
对象驱动的。这个过程由两阶段组成:
LoginContext
使用其公共的 no-arg 构造器创建每个已配置的LoginModule
。- 每个
LoginModule
都是通过其 initialize 方法来初始化的。Subject
参数需保证是非 null 的。initialize 方法的签名是:public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
。 login
被调用来启动验证过程。例如,某个方法实现可能提示用户输入用户名和密码,然后根据存储在命名服务(如 NIS 或 LDAP)里的数据进行检验。其他的实现可能连接智能卡和生物设备,或者简单地从底层操作系统抽取信息。每个LoginModule
对用户标识符的检验会在 JAAS 验证的第一阶段来考虑。login
方法的签名是boolean login() throws LoginException
。LoginException
表示失败。返回值为 true 表示方法调用成功,而 false 表示登录模块应该被忽略。- 如果
LoginException
的总体验证成功,commit
将在每个LoginModule
上调用。如果LoginModule
的第一阶段验证成功,commit 方法将继续第二阶段并将相关的主体、公共凭证和/或私有凭证和关联主题。如果LoginModule
的第一阶段失败,那commit
将删除任何之前保存的验证状态,如用户名或密码。commit
方法的签名是:boolean commit() throws LoginException
。LoginException
的抛出表示完成提交阶段失败。返回值为 true 表示方法调用成功,而 false 表示登录模块应该被忽略。 - 如果
LoginException
的总体验证失败,abort
将在每个LoginModule
上调用。abort
方法删除或销毁任何之前 login 或 initialize 方法创建的验证状态。abort
方法的签名是:boolean abort() throws LoginException
。LoginException
的抛出表示完成abort
阶段失败。返回值为 true 表示方法调用成功,而 false 表示登录模块应该被忽略。 - 要在成功登录后删除验证状态,应用程序将调用
LoginContext
上的logout
。这会导致在每个LoginModule
上调用logout
方法。logout
方法删除原来在commit
阶段和主题相关联的主体和凭证。凭证应该在删除时被销毁。logout
方法的签名是:boolean logout() throws LoginException
。LoginException
的抛出表示完成登出阶段失败。返回值为 true 表示方法调用成功,而 false 表示登录模块应该被忽略。
当
LoginModule
必须和用户进行通讯来获取验证信息时,它使用 CallbackHandler
对象。应用程序实现 CallbackHandler 接口并将其传入 LoginContext
,这会直接发送验证信息到底层的等录模块。
登录模块使用
CallbackHandler
来获取用户的输入,如密码或智能卡 PIN,并提供信息给用户,如状态信息。通过允许应用程序指定 CallbackHandler
,底层的 LoginModule
保持对应用程序和用户交互的独立。例如,GUI 应用程序的 CallbackHandler
实现可能显示一个窗口来让用户输入。另一方面,非 GUI 的应用程序可能 CallbackHandler
简单地使用应用服务器的 API 来获取凭证信息。 CallbackHandler 接口有一个方法需要被实现:
void handle(Callback[] callbacks) throws java.io.IOException, UnsupportedCallbackException;
void handle(Callback[] callbacks)
throws java.io.IOException,
UnsupportedCallbackException;
Callback
接口是我们最后将了解的验证类。它是为几个默认实现提供的一个标记接口,其中包括 NameCallback
和在以前示例里使用的 PasswordCallback
。LoginModule
使用 Callback
来请求验证机制所需的信息。在验证的登录阶段,LoginModule
将一个 Callback
队列直接传入 CallbackHandler.handle
方法。如果 callbackhandler
无法理解如何使用传递到 handle 方法的 Callback
,它会抛出 UnsupportedCallbackException
来中止 login 的调用。