java 简化判断

    技术2024-03-20  87

    您的企业是否运行许多共存的Java应用程序,每个应用程序都需要进行身份验证才能访问企业资源? 如果是这样,您可能希望实现单点登录(SSO)安全功能,以使身份验证对用户的干扰减少。 在本文中,您将学习如何使用Kerberos和Java通用安全服务API(GSS-API)来实现SSO。 首先,我们将介绍SSO的含义并说明其潜在的应用。 然后,我们将探讨实现基于Kerberos的SSO时发生的消息交换的顺序。 接下来,我们将简要介绍Java GSS-API和使用GSS完成SSO的典型Java应用程序的体系结构。 最后,我们将各个部分放在一起并提供有效的代码示例,以演示Java开发人员如何使用GSS Kerberos票证实现SSO。

    什么是单点登录?

    从根本上说,单点登录身份验证意味着共享身份验证数据。 例如,一家仓库公司的许多员工可能需要访问企业资源(例如数据库表)以满足他们的工作要求,而不同的员工则根据他们的工作职能需要不同的资源。 客户经理可能只需要访问与帐户相关的数据库表,而销售经理可能需要访问与销售相关的数据库表。 相反,公司的首席执行官可能需要访问企业数据库中的任何表。

    显然,该企业需要适当的身份验证机制,该机制可以确定哪个员工正在尝试访问特定资源。 一旦企业认证模块知道了雇员的身份,企业实施中的授权模块就可以检查经过认证的用户是否具有访问资源的必要特权。

    关于代码

    文件j-gss-sso.zip包含本文中使用的所有代码。 本文中的每个清单都以带有文件名的注释开头; 使用这些名称将每个列表与j-gss-sso.zip存档中的相应文件进行匹配。

    这个档案还包括Setup.txt文件,我在本文的几处都提到了该文件。 在尝试执行本文的代码之前,请阅读此文件。

    假设员工使用其用户名和密码进行身份验证。 因此,企业的身份验证模块将具有用户名和密码的数据库。 每个传入的身份验证请求都将带有一个用户名-密码对,身份验证模块会将其与内部数据库中的用户名/密码对进行比较。

    现在,我们的仓储公司可能在其范围内运行了多个应用程序。 不同的应用程序构成同一企业的不同模块。 每个应用程序本身都是完整的,这意味着它具有自己的用户群以及几个不同的层,包括后端数据库,业务逻辑和为其用户提供的GUI。 企业应用程序集成 (EAI)是涉及将此类独立应用程序集成到企业中的项目的流行名称。

    一个共同的因素通常标志着EAI项目中的身份验证过程:特定应用程序的用户需要访问企业内的另一个应用程序。 例如,使用销售数据库的销售经理可能需要访问库存数据库,以检查特定组件的可用性。 我们如何启用这种跨应用程序身份验证?

    我们有两个选择:

    我们可以在两个应用程序上复制用户名和密码数据库,从而有效地允许两个应用程序为我们的仓储公司的所有员工处理身份验证请求。 这也意味着用户将在两个应用程序上分别进行身份验证-换句话说,他将在访问两个应用程序中的任何一个时输入其用户名和密码,并且该应用程序将执行所有身份验证步骤。 这意味着我们不仅在复制用户名和密码数据库:还在复制身份验证过程的开销。 此解决方案中的冗余量应该非常明显。 第二种选择是通过销售和库存应用程序之间的单点登录来启用身份验证数据共享。 如果用户在一个应用程序上通过了身份验证,则其身份验证信息将传输到第二个应用程序。 第二应用程序照原样接受认证信息,而无需经历所有认证步骤。 此解决方案中没有冗余。 唯一的要求是两个应用程序相互信任,以便每个应用程序接受来自彼此的身份验证数据。

    通常,SSO被实现为单独的身份验证模块。 所有需要对用户进行身份验证的应用程序都依赖于基于SSO的身份验证模块来检查其用户身份。 然后,基于此身份验证信息,不同的应用程序将实施自己的授权策略。

    希望我们的仓储公司示例能够从用户的角度说明SSO的外观。 自然,下一个问题是:我们如何实施 SSO? 我们有几种方法可以做到这一点。 在下一节中,我们将讨论Kerberos,它提供各种安全服务,包括SSO。

    对SSO使用Kerberos

    Kerberos是Internet工程任务组(IETF)标准,它定义了典型的密钥交换机制。 应用程序可以使用Kerberos服务来验证其用户并与他们交换加密密钥。 Kerberos基于票证的概念。 票证只是包装密码密钥和其他一些信息的数据结构。 密钥分发中心 (KDC)将Kerberos票证分发给经过身份验证的用户。 KDC发行两种类型的票证:

    主票,也称为票授予票 (TGT) 服务票

    KDC首先向客户端发出TGT。 然后,客户可以针对他或她的TGT请求几张服务票。 为了解释TGT和服务票证的工作方式,让我们考虑以下密钥交换方案:

    客户端向KDC发送一条消息,请求发出TGT。 该请求是在纯文本形式(没有任何加密),和包括客户端的用户名,但不包括其密码。 KDC向客户端发出TGT。 TGT包含加密形式的会话密钥。 为了加密会话密钥,KDC使用从客户端密码派生的密钥。 这意味着只有客户端可以解密TGT并获取会话密钥。 因此,尽管客户端应用程序不需要知道密码即可请求TGT,但确实需要密码来处理或使用TGT。 客户端解密TGT并从中提取会话密钥。 然后,客户编写服务单请求。 服务票证仅对两方之间的通信有效,即在客户端与客户端要与之通信的另一个实体(例如,服务器)之间进行通信。 没有其他人可以使用服务票。 因此,在请求服务票证时,客户端会指定要使用该服务票证的服务器的名称。 该服务器应该已经在KDC中注册。 KDC为服务器编写服务票证。 该票证包含客户的身份验证数据和一个新的加密密钥,称为子会话密钥。 KDC用服务器的秘密密钥加密服务票证(秘密密钥是KDC和服务器之间的共享秘密)。 这意味着只有服务器可以解密服务票证。 KDC编写一条消息并将服务票证包装在其中。 KDC还会在消息中复制子会话密钥。 请注意,子会话密钥现在在消息中包含两次:一次直接在消息中,另一次在服务票证中。 KDC使用来自步骤2和3的会话密钥对完整的消息进行加密。因此,只有客户端才能解密该消息并提取子会话密钥以及服务票证。 但是客户端无法解密服务票证-只有服务器可以解密。 因此,没有其他人可以将服务票证用于任何目的。 然后,KDC将消息发送到客户端。 客户端解密从KDC接收到的消息,并获取消息中的子会话密钥以及服务票证。 它将服务票证发送到服务器。 服务器接收服务票证并将其解密,以获取发出请求的客户端的身份验证数据以及子会话密钥。 然后,服务器确认客户端的请求,并在客户端和服务器之间建立新的安全会话。 现在,客户端和服务器都拥有相同的子会话密钥,它们可以用于彼此之间的安全通信。

    客户端可以为另一个服务器应用程序重复步骤3到8。 这意味着我们的Kerberos服务可用于共享身份验证数据,并且同一客户端(代表单个用户)可以通过不同的应用程序进行身份验证。 这有效地启用了SSO。

    我们刚刚描述的过程说明了任何应用程序如何使用Kerberos进行身份验证。 现在,我们将研究如何专门使用Java平台来利用Kerberos的功能。

    Java通用安全服务API

    IETF已将通用安全服务API(GSS-API)定义为提供诸如机密性,消息完整性和身份验证之类的功能的高级安全API。 GSS-API可以轻松地在客户端-服务器环境中工作。 IETF以与语言无关的方式定义了GSS-API。

    Java GSS-API是GSS-API的特定于Java语言的形式。 Sun通过Java Community Process开发了Java GSS-API,并且还提供了参考实现,该实现与JDK 1.4版捆绑在一起。 请参阅相关信息的链接,官方的RFC的GSS-API。

    设置KDC

    GSS中的Kerberos实现不需要特定的KDC实现。 因此,所有基于GSS的代码都可以与任何符合GSS的KDC一起使用。 请参阅相关主题流行KDC实现的列表。

    为了测试本文中的代码,我使用了Microsoft的KDC实现,该实现是Windows 2000 Server(以及基于Windows的服务器的更高版本)中的一项服务。 文件j-gss-sso.zip(包含本文的所有代码)也包含文件Setup.txt,该文件概述了设置Microsoft KDC的步骤。

    Microsoft KDC接受所有Windows用户作为服务的用户。 这意味着KDC可以为在Windows网络中注册的任何用户颁发TGT。

    本文仅讨论Java GSS-API。 因此,为简单起见,在本文的其余部分中,我们将Java GSS-API称为GSS 。

    GSS的目的是在不同的低层安全服务之上提供高层抽象层。 Kerberos是在技术,您可以在GSS抽象下使用的一个,但也有许多人,喜欢简单公钥机制(SPKM;看到相关信息 ),我们将不会在本文中讨论。 GSS抽象层允许程序员开发安全的应用程序,而不必担心哪种机制可以在较低级别上提供安全服务。 在本文中,我们将重点介绍在GSS下使用Kerberos作为实现SSO的低级安全机制。

    在本节的其余部分,我们将对GSS进行非常简单的介绍。 下一节将演示实际的API。 在即将进行的讨论中,我们将经常使用术语“ 通信实体或对等体 ”。 实体或对等体是使用GSS进行通信的应用程序。 请求对等方将充当客户端,并请求新会话以与服务对等方安全通信。 服务对等方将充当服务器并接受或拒绝该请求。

    GSS GSSName , GSSCredential和GSSContext 。 GSSName会将您标识为个人,就像您在检查电子邮件时输入的用户名一样。 GSSCredential是您提供的用于证明您的身份的东西,例如您在检查电子邮件时输入的密码。 Kerberos服务票证是GSSCredential另一个示例。 GSSContext就像一个安全会话,其中封装了与安全相关的信息,例如加密密钥。 基于GSS的应用程序将使用GSSContext彼此安全地通信。

    现在,让我们将到目前为止所学的概念付诸实践。

    GSS客户端和服务器应用程序

    在本节中,我们将演示基于GSS的安全Java应用程序的实际实现。 我们将开发两个可重用的JavaBeans组件。 一个bean充当请求客户端,并请求发起新的GSS会话(GSS会话称为GSS上下文 )。 另一个bean将充当服务器,侦听请求,并接受来自客户端的传入请求。 然后,它建立安全上下文并与客户端通信。

    当我们在本文中讨论和演示SSO时,将由第三方托管的应用程序使用KDC来简化我们的应用程序。 客户端和服务器都信任此KDC,并接受来自它的身份验证数据。 因此,您将需要运行Kerberos实现才能使用本文中的示例代码,但是您可以使用任何想要的GSS兼容KDC。 (如果使用的是最新的Windows服务器平台,则已经可以访问合适的Kerberos实现;有关更多详细信息,请参见标题为“ 设置KDC ”的边栏。)

    JAAS认证客户端

    一旦运行了KDC服务,就可以继续并请求KDC发出TGT。 请求的GSS客户端(想要与远程服务对等方建立安全的GSS上下文的客户端)将要求KDC发出TGT。

    但是,我们这里有一个小问题。 GSS不包含任何从用户获取用户名-密码对的方法。 因此,GSS应用程序必须依赖于其他非GSS机制来获取登录信息。 我们将使用Java身份验证和授权服务(JAAS)来允许发出请求的客户端提供用户名和密码并获取TGT。

    看一下清单1 ,它显示了一个名为GSSClient的类。 此类表示想要与远程GSS服务器建立安全会话的GSS客户端的功能。 GSSClient构造函数采用许多参数:

    客户端对等体的名称 客户端对等体的密码 远程服务对等方的名称 远程服务对等方的地址 服务器的端口 Kerberos领域或域(运行KDC的域;有关如何为Microsoft的KDC指定领域的详细信息,请参见Setup.txt) KDC的地址 登录配置文件的位置路径和名称(我们将在稍后对此进行说明) 客户端配置的名称(指定客户端要使用的身份验证机制)

    注意:在我们与客户的讨论中,我们将重复引用清单1 ,因此您可能需要保持窗口打开以进行指导。

    注意main()方法模拟一个简单的应用程序。 我们在清单1中包含了此方法,只是为了通过从命令行运行此类来演示此类的操作。 main()方法按照上述顺序从命令行读取参数值,然后调用GSSClient()构造函数,将参数值传递给构造函数。

    GSSClient()构造函数将来自命令行的参数存储在不同的字段中,并且还自行设置了三个系统属性。 java.security.krb5.realm系统属性指定KDC领域; java.security.krb5.kdc属性指定KDC服务器的地址; 而java.security.auth.login.config属性指定登录配置文件的位置路径和名称。 GSS框架将在内部使用这些属性。

    实例化GSSClient对象后, main()方法将调用GSSClient.login()方法。 此方法实例化LoginContext对象,该对象是JAAS框架的一部分。 LoginContext构造函数采用两个参数。 第一个confName带有第九个命令行参数值。 第二个对象是名为BeanCallBackHandler的类的对象。 让我们更详细地看一下这两个参数的用法。

    confName和配置文件 confName带有JAAS配置的名称。 第九个命令行参数(客户端配置的名称)指定客户端要用于身份验证的JAAS配置。 由第八个命令行参数指定的JAAS配置文件包含一个或多个客户端配置,客户端可以从中使用任何一种。

    JAAS配置指定了将用于认证的机制。 配置文件的概念允许Java应用程序选择独立于身份验证逻辑的身份验证机制。

    JAAS配置存储为.conf文件。 清单2中的示例JAAS配置文件具有两个配置。 GSSClient配置如下所示:

    GSSClient { com.sun.security.auth.module.Krb5LoginModule required; };

    此JAAS配置指定Java类com.sun.security.auth.module.Krb5LoginModule的名称。 此类是JAAS中的Kerberos登录模块, GSSClient配置将其用于登录。 因此,指定此配置意味着我们将使用Kerberos作为身份验证机制。

    清单2. GSSClient和GSSServer的JAAS登录配置
    /**** Login.conf ****/ GSSClient{ com.sun.security.auth.module.Krb5LoginModule required; }; GSSServer{ com.sun.security.auth.module.Krb5LoginModule required storeKey=true; };

    JAAS身份验证逻辑的详细信息不在本文讨论范围之内。 如果要了解更多信息,请在“ 相关主题”部分中查看教程“ Java安全性,第2部分:身份验证和授权”的链接 。

    BeanCallBackHandler 现在,回到清单1中对客户机的讨论中,我们与LoginContext构造函数方法调用一起传递的第二个参数( BeanCallBackHandler类的实例)指定了将在身份验证过程中处理回调的对象。 Callback允许Java应用程序在认证过程中与JAAS实现进行交互。 通常,您将使用回调功能将用户名和密码传递给Kerberos身份验证模块。 请注意,我们已经在BeanCallBackHandler构造函数调用中传递了username和password 。

    现在看清单3,它显示了我们的BeanCallBackHandler类。 此类实现一个名为CallBackHandler的接口,该接口是JAAS框架的一部分,仅包含一个名为handle() 。

    清单3.处理来自JAAS框架的回调
    /**** BeanCallbackHandler.java ****/ import java.io.*; import java.security.*; import javax.security.auth.*; import javax.security.auth.callback.*; public class BeanCallbackHandler implements CallbackHandler { // Store username and password. String name = null; String password = null; public BeanCallbackHandler(String name, String password) { this.name = name; this.password = password; }//BeanCallbackHandler public void handle (Callback[] callbacks) throws UnsupportedCallbackException, IOException { for(int i=0; i<callbacks.length; i++) { Callback callBack = callbacks[i]; // Handles username callback. if (callBack instanceof NameCallback) { NameCallback nameCallback = (NameCallback)callBack; nameCallback.setName(name); // Handles password callback. } else if (callBack instanceof PasswordCallback) { PasswordCallback passwordCallback = (PasswordCallback)callBack; passwordCallback.setPassword(password.toCharArray()); } else { throw new UnsupportedCallbackException(callBack, "Call back not supported"); }//else }//for }//handle }//BeanCallbackHandler

    CallBackHandler对象的handle()方法将在身份验证期间自动接收控制。

    JAAS框架将CallBack对象数组传递给CallBackHandler实例的handle()方法,该实例作为LoginContext构造函数的第二个参数值传递。 CallBack对象数组包含不同类型的CallBack对象。 但是,我们只对两种类型感兴趣: NameCallBack和PasswordCallBack ,它们都扩展了基本的CallBack类。

    NameCallBack对象用于向JAAS框架提供username ,而PasswordCallBack则在身份验证期间携带password 。 里面的handle()的方法清单3中,我们简称为setName()的方法NameCallBack对象和setPassword()的方法PasswordCallBack对象。

    总结上面有关LoginContext构造函数调用的两个参数的讨论,可以说我们已经将所有信息提供给LoginContext对象,这是用户身份验证所需的。 因此,清单1中的下一步应该是调用LoginContext.login()方法,该方法将执行实际的登录过程。

    如果在身份验证过程中出了点问题(例如,如果password不正确),则会抛出javax.security.auth.login.LoginException 。 如果没有因登录方法调用而导致的异常,那么我们可以假定身份验证成功。

    因为我们使用的是Kerberos登录,所以JAAS框架在内部管理与KDC的所有通信并获取Kerberos TGT,从而将所有技术细节隐藏在易于使用的高级JAAS界面下。

    成功的身份验证将导致在LoginContext对象中加载Kerberos TGT。 您可以调用LoginContext类的getSubject()方法,该方法返回一个名为Subject的类的实例。 Subject实例包装了TGT。

    我们将使用此Subject类执行已登录的操作:建立安全的GSS上下文。 让我们看看成功认证后如何调用所需的操作。

    Subject类包含一个名为doAs()的静态方法,该方法带有两个参数。 看一下清单1中的Subject.doAs()方法调用。第一个参数值是我们在成功认证后刚获得的Subject实例。 doAs()方法将使用此经过身份验证的Subject来做出授权决定-也就是说,它将使用它来判断此Subject (我们已经过身份验证)是否有权调用给定操作。

    我们仅使用Subject类来获取TGT。 因此,我们没有为GSSClient指定任何授权策略。 任何用户都可以运行此客户端。 GSSClient不需要任何安全授权即可执行给定的操作。 但是,诸如applet之类的Web客户端在严格的安全上下文中运行,这需要安全授权才能成功执行。 Setup.txt包含有关为基于applet的GSS客户端编写授权策略的简要说明,我们将在本文后面详细介绍此类客户端。

    doAs()方法调用的第二个参数值this ,指定了我们正在讨论的GSSClient对象(清单1)。 此参数需要一个暴露PriviledgedAction接口的对象。 注意,我们的GSSClient实现了PriviledgedAction接口。 我们将所有客户端代码组合到一个类中,但是如果您愿意,您可以有一个单独的类来实现PriviledgedAction接口。 如果选择这样做,则将实例化该对象并通过doAs()方法调用将其作为第二个参数值传递。

    PriviledgedAction接口仅包含一个名为run() 。 如果授权策略允许Subject访问GSSClient类的run()方法,则该方法将在单独的执行线程中接收控制。 当run()方法收到控制权时,安全性上下文(本质上是TGT)随之而来。 我们的GSS逻辑将自动使用该安全上下文并获取TGT。

    设计GSS客户端

    我们必须在清单1的run()方法中执行以下步骤以建立GSS会话:

    实例化GSSManager对象。 注意,在清单1中,我们已经调用了GSSManager类的getInstance()静态方法,该方法返回一个GSSManager对象。 GSSManager对象将至少支持Kerberos机制,也许还支持其他一些机制。 GSSManager类包含GSS应用程序可以调用以指定其他安全机制的方法。 但是,因为我们只关注Kerberos机制,所以我们可以简单地调用getInstance()方法来使用Kerberos机制。 我们将不讨论其他机制的细节。 因此,在实例化清单1中的GSSManager对象之后,我们实例化了一个名为kerberos的Oid (对象ID)对象,该对象标识Kerberos机制。 本质上,无论我们要说什么,我们都将传递该对象,“我们希望使用Kerberos作为GSS层下面的基础技术。” 创建一个代表GSS实体的GSSName对象。 在双向通信期间,您可以将GSS实体视为一个通信对等体。 因此,我们实际上将创建两个GSSName对象:一个用于clientPeerName请求的客户端对等体( clientPeerName ),另一个用于远程对等体( remotePeerName )。 创建一组凭据。 GSS是一种通用的安全机制,因此它必须依赖于基础技术来创建这些凭据。 因为我们使用的是Kerberos,所以Kerberos票证是实际的凭证。 要获取GSSCredential对象,我们使用GSSManager类的createCredential()方法。 createCredential()方法返回一个对象,该对象公开GSSCredential接口。 创建安全的GSS上下文,该上下文将用于在两个通信对等方之间建立安全的通信。 GSSManager的createContext()方法创建并返回GSSContext接口的实例。 GSSContext对象包装了GSS客户端希望使用Kerberos服务与远程对等GSSContext的实际安全上下文。 请注意,在清单1中,我们已经将在步骤2和3中创建的GSSName和GSSCredential对象传递给createContext()方法调用。

    现在,我们有了GSSContext对象,该对象包装了安全上下文,但是上下文本身尚未建立。 获取GSSContext对象后,发出请求的对等方将调用其requestConf()方法。 此方法导致应用程序请求机密性和数据完整性,因此该应用程序要发送到远程服务器的任何数据都将采用加密形式。

    在调用清单1中的requestConf()方法之后,我们声明了一个名为byteToken的字节数组,并将其实例化为零字节。 byteToken数组将保存GSS客户端将发送到服务器和从服务器接收的数据字节。

    现在,我们在while循环中重复调用GSSContext接口的initSecContext()方法。 此方法执行字节的实际交换,以在请求和服务对等方之间建立安全上下文。

    清单1中的while(!peerContext.isEstablished())块执行了GSS客户端和服务器之间的实际双向通信。 仅当建立安全上下文时(否则发生错误以防发生异常),此块才退出。

    首次执行while循环时, byteToken数组将没有数据。 由于尚未建立安全上下文,所以peerContext.isEstablished()方法将返回false 。

    在while循环中,我们要做的第一件事是将byteToken数组传递给initSecContext()方法。 此方法执行两个作业:它生成客户端发送到服务器的字节,并且还接受来自服务器的字节。 字节交换一直进行到建立GSS上下文为止。

    自然,当我们第一次调用initSecContext()方法时,没有字节可以传递给该方法。 因此,我们将空的byteToken数组传递给第一次调用的方法。 initSecContext()方法返回一些字节,这些字节已存储在相同的byteToken数组中。 接下来,我们将byteToken (从initSecContext()方法调用中获取)写入输出流,并刷新流,以便将byteToken数组发送到远程服务器。

    现在,我们期望远程服务器将发送一些内容,以响应我们发送给它的字节。 因此,我们从输入流中读取字节,将字节存储在相同的byteToken数组中,然后将byteToken数组传递回initSecContext()方法。 该方法再次返回字节数组,我们将其发送到远程服务器。 字节交换在while循环内进行while直到建立安全上下文并且peerContext.isEstablished()方法返回true peerContext.isEstablished() 。

    建立安全上下文意味着现在已经在客户端和服务器上都提供了适当的Kerberos密钥。 双方都可以使用这些密钥进行安全通信。 建立安全上下文后,我们仅返回GSSContext对象。

    使用GSS上下文

    现在,我们将在GSSClient类的main()方法中看到如何使用GSSContext对象。 调用login()方法后,我们检查一下login()方法返回的GSSContext是否为空。 如果不是,我们确定可以使用安全上下文与远程服务器进行安全通信。

    然后, main()方法将检查远程服务器是否满足了客户机的机密性和消息完整性请求(请注意,客户机在开始建立GSS上下文之前发出了此请求)。 如果服务器满足了我们的请求,则GSSContext类的getConfState()方法将返回true 。

    main()方法现在可以将所需的任何数据发送到远程服务器。 我们已经编写了sendMessage()帮助器方法来将数据发送到服务器。 请注意,在此方法中, GSSContext类的wrap()方法采用字节数据数组并返回字节数组。 sendMessage()方法将纯文本数据提供给wrap()方法,并作为响应获取加密的字节数组。 现在,我们可以通过将加密的字节数组写入输出流来将其发送到远程服务器。

    然后, sendMessage()方法侦听来自服务器的传入数据。 当我们从服务器接收到一些数据时,我们可以将传入的数据传递给GSSContext类的unwrap()方法,该GSSContext返回数据的纯文本格式。

    我们的main()和sendMessage()方法是用于在安全GSS会话上进行数据交换的特定于应用程序逻辑的非常简单的示例。 您可以使用相同的概念来构建自己的应用程序逻辑。

    GSS服务器应用程序

    我们已经了解了我们的GSS客户端应用程序如何工作。 现在,让我们构建一个服务器与之交互。

    看一下清单4 ,它显示了GSSServer类的代码。 (同样,在讨论服务器时,您将要保持此列表窗口处于打开状态。) GSSServer的startServer()方法执行与我们在解释GSSClient类的login()方法时已经讨论的功能相同的功能。

    现在,在GSSServer类的run()方法内部,我们创建了代表服务器的GSSManager和GSSName对象。 到目前为止,客户端和服务器端代码之间几乎没有任何区别。 但是仔细一点,会发现服务器仅创建一个代表服务器的GSSName对象。 创建此GSSName ,服务器将调用createCredential()方法以将其凭据加载到GSSCredential对象中。

    下一步是调用createContext()对象来创建新的GSS上下文。 对createContext()方法的调用与我们在GSS客户端应用程序中进行的createContext()调用不同。 这次, createContext()方法仅采用一个参数:服务器的凭据。 这意味着该服务器端上下文不在两方之间。 这就像一个开放式连接,其中仅确定两个通信方之一。

    Next, we create input and output streams for communication, and then enter into a while loop that will keep on looping until a secure context is established with a requesting client. Inside this loop, we wait for the requesting client to send a connection establishment request. When we receive some data on the input stream inside the while loop, we read the data into a byte array and provide the byte array token to the acceptSecContext() method of the GSSContext class. The acceptSecContext() method returns some data bytes to us, which we send back on the output stream.

    The initSecContext() and acceptSecContext() methods of the GSSContext class work in combination. We demonstrated the use of the initSecContext() method while discussing our GSS client application. The initSecContext() method produces the initial bytes that a GSS client sends to a GSS server application. The acceptSecContext() method accepts these incoming bytes and generates its own byte array, which we send back to the client. This exchange of bytes continues until a secure GSS context is established.

    Therefore, GSS handles all communication as byte array tokens. You can use any type of transport service to convey the array of bytes from the client to the server and back. GSS is indifferent to the transport facility you use for data transmission.

    The process of establishing a secure session concludes with the authentication of the requesting client. You can call the getSrcName() method of the GSSContext class to fetch the GSSName of the authenticated client. On the other hand, the getTargName() method of the GSSContext class returns the GSSName of the server that accepted the remote client's request.

    After the while (!context.isEstablished()) loop returns in Listing 4, the run() method waits for communication from the client. It keeps on listening for incoming data, and when it receives any incoming byte string, it hands that string over to the unwrap() method of the GSSContext class. The unwrap() method will return the plain text form of the message from the client.

    Just for the sake of demonstration, in our sample code we pass a message answer string back to the requesting client. Application developers can implement their own application logic to send their application-specific data back to the requesting client.

    We have demonstrated the basic forms of the GSS client and server-side application development. You can put our sample applications to use in several different scenarios. For example, you could use our GSS client as part of a JSP or a JFC application. One interesting scenario would be the use of our GSS client as part of a Java applet running inside a browser, which we'll cover in some detail next.

    GSS in a browser

    Have a look at Listing 5 , which demonstrates how an applet can use the GSS client in Listing 1 to establish secure communication with the GSS server in Listing 4 .

    The applet will run in an HTML page such as the one shown in Listing 6:

    Listing 6. An HTML page that uses a GSS applet
    <!-- E-Commerce Login.html --> <HTML> <HEAD> <TITLE>E-Commerce Login... </TITLE> </HEAD> <BODY> <p align="center"> <table bgcolor="Gray"> <tr> <td align="center"> <b>E-Commerce Site Login Page </b> </td> </tr> <tr> <td> <Applet CODE="GSSClientApplet.class" archive="GSSClientApplet.jar" name="GSSClientApplet" width="500" height="280"> </Applet> </td> </tr> </table> </p> </BODY> </HTML>

    Let's assume that this applet is running on the main page of an e-commerce Web site. Figure 1 shows what it would look like in action. There are two text entry fields, three buttons, and a text area. Each of the three buttons corresponds to the server-side implementation of a partner of the e-commerce Web site.

    Figure 1. An HTML page showing GSS applet usage

    A customer of the e-commerce Web site can authenticate himself with any of the site's partners. The customer will enter his username and password in the text fields of the applet, and press the Login button corresponding to the partner site with which he wants to authenticate. The button event handlers simply provide the required parameters to the GSSClient constructor. The rest of the work is our GSS client's job, which we have already explained.

    摘要

    We have discussed single sign-on, Kerberos, and GSS in this article. Applications can use SSO to share authentication data with each other. Kerberos offers a mechanism for key management and exchange. GSS is a high-level security API that works on top of different security services.

    As you've seen here, you can use GSS and Kerberos to build SSO solutions for the Java platform. While the concepts may seem complex at first, the layers of abstraction afforded by GSS should allow you to implement SSO fairly easily in your Java platform-based enterprise. I hope that you can use the code outlined here as a jumping-off point for your own projects.


    翻译自: https://www.ibm.com/developerworks/java/library/j-gss-sso/index.html

    相关资源:跨服务器登录验证(单点登录SSO)的过程和Java实现
    Processed: 0.013, SQL: 9