摘要
Java Authentication Authorization Service(JAASJava验证和授权API)提供了灵活和可伸缩的机制来保证客户端或服务器端的Java程序Java早期的安全框架强调的是通过验证代码的来源和作者保护用户避免受到下载下来的代码的攻击JAAS强调的是通过验证谁在运行代码以及他/她的权限来保护系统面受用户的攻击它让你能够将一些标准的安全机制例如Solaris NIS(网络信息服务)Windows NTLDAP(轻量目录存取协议)Kerberos等通过一种通用的可配置的方式集成到系统中本文首先向你介绍JAAS验证中的一些核心部分然后通过例子向你展示如何开发登录模块
你是否曾经需要为一个应用程序实现登录模块呢?如果你是一个比较有经验的程序员相信你这样的工作做过很多次而且每次都不完全一样你有可能把你的登录模块建立在Oracle数据库的基础上也有可能使用的是NT的用户验证或者使用的是LDAP目录如果有一种方法可以在不改变应用程序级的代码的基础上支持上面提到的所有这一些安全机制对于程序员来说一定是一件幸运的事
现在你可以使用JAAS实现上面的目标JAAS是一个比较新的的Java API在JSE 中它是一个扩展包在JSE 中变成了一个核心包在本文中我们将介绍JAAS的一些核心概念然后通过例子说明如何将JAAS应用到实际的程序中本文的例子是根据我们一个基于Web的Java应用程序进行改编的在这个例子中我们使用了关系数据库保存用户的登录信息由于使用了JAAS我们实现了一个健壮而灵活的登录和身份验证模块
Java验证和授权概论
在JAAS出现以前Java的安全模型是为了满足跨平台的网络应用程序的需要而设计的在Java早期版本中Java通常是作为远程代码被使用例如Applet因此最初的安全模型把注意力放在通过验证代码的来源来保护用户上早期的Java安全机制中包含的概念如SercurityManager沙箱概念代码签名策略文件多是为了保护用户
JAAS的出现反映了Java的演变传统的服务器/客户端程序需要实现登录和存取控制JAAS通过对运行程序的用户的进行验证从而达到保护系统的目的虽然JAAS同时具有验证和授权的能力在这篇文章中我们主要介绍验证功能
通过在应用程序和底层的验证和授权机制之间加入一个抽象层JAAS可以简化涉及到Java Security包的程序开发抽象层独立于平台的特性使开发人员可以使用各种不同的安全机制而且不用修改应用程序级的代码和其他Java Security API相似JAAS通过一个可扩展的框架服务提供者接口(Service Provider InterfaceSPI)来保证程序独立于安全机制服务提供者接口是由一组抽象类和接口组成的图一中给出了JAAS程序的整体框架图应用程序级的代码主要处理LoginContext在LoginContext下面是一组动态配置的LoginModulesLoginModule使用正确的安全机制进行验证
图一给出了JAAS的概览应用程序层的代码只需要和LoginContext打交道在LoginContext之下是一组动态配置的LoginModule对象这些对象使用相关的安全基础结构进行验证操作
图一 JAAS概览
JAAS提供了一些LoginModule的参考实现代码比如JndiLoginModule开发人员也可以自己实现LoginModule接口就象在我们例子中的RdbmsLonginModule同时我们还会告诉你如何使用一个简单的配置文件来安装应用程序
为了满足可插接性JAAS是可堆叠的在单一登录的情况下一组安全模块可以堆叠在一起然后被其他的安全机制按照堆叠的顺序被调用
JAAS的实现者根据现在一些流行的安全结构模式和框架将JASS模型化例如可堆叠的特性同Unix下的可堆叠验证模块(PAMPluggable Authentication Module)框架就非常相似从事务的角度看JAAS类似于双步提交(TwoPhase CommitPC)协议的行为JAAS中安全配置的概念(包括策略文件(Police File)和许可(Permission))来自于JSE JAAS还从其他成熟的安全框架中借鑒了许多思想
客户端和服务器端的JAAS
开发人员可以将JAAS应用到客户端和服务器端在客户端使用JAAS很简单在服务器端使用JAAS时情况要复杂一些目前在应用服务器市场中的JAAS产品还不是很一致使用JAAS的JEE应用服务器有一些细微的差别例如JBossSx使用自己的结构将JAAS集成到了一个更大的安全框架中而虽然WebLogic x也使用了JAAS安全框架却完全不一样
现在你能够理解为什么我们需要从客户端和服务器端的角度来看JAAS了我们将在后面列出两种情况下的例子为了使服务器端的例子程序更加简单我们使用了Resin应用服务器
核心JAAS类
在使用JAAS之前你首先需要安装JAAS在JSE 中已经包括了JAAS但是在JSE 中没有如果你希望使用JSE 你可以从SUN的官方站点上下载JAAS当正确安装了JAAS后你会在安装目录的lib目录下找到jaasjar你需要将该路径加入Classpath中(注如果你安装了应用服务器其中就已经包括了JAAS请阅读应用服务器的帮助文档以获得更详细的信息)在Java安全属性文件javasecurity中你可以改变一些与JAAS相关的系统属性该文件保存在<jre_home>/lib/security目录中
在应用程序中使用JAAS验证通常会涉及到以下几个步骤
创建一个LoginContext的实例
为了能够获得和处理验证信息将一个CallBackHandler对象作为参数传送给LoginContext
通过调用LoginContext的login()方法来进行验证
通过使用login()方法返回的Subject对象实现一些特殊的功能(假设登录成功)
下面是一个简单的例子
LoginContext lc = new LoginContext(MyExample);
try {
lclogin();
} catch (LoginException) {
// Authentication failed
}
// Authentication successful we can now continue
// We can use the returned Subject if we like
Subject sub = lcgetSubject();
SubjectdoAs(sub new MyPrivilegedAction());
在运行这段代码时后台进行了以下的工作
当初始化时LoginContext对象首先在JAAS配置文件中找到MyExample项然后更具该项的内容决定该加载哪个LoginModule对象(参见图二)
在登录时LoginContext对象调用每个LoginModule对象的login()方法
每个login()方法进行验证操作或获得一个CallbackHandle对象
CallbackHandle对象通过使用一个或多个CallBack方法同用户进行交互获得用户输入
向一个新的Subject对象中填入验证信息
我们将对代码作进一步的解释但是在这之前让我们先看代码中涉及到的核心JAAS类和接口这些类可以被分为三种类型
普通类型 SubjectPrincipal凭证
验证 LoginContextLoginModuleCallBackHandlerCallback
授权 PolicyAuthPermissionPrivateCredentialPermission
上面列举的类和接口大多数都在javaxsecurityauth包中在JSE 中还有一些接口的实现类在comsunsecurityauth包中
普通类型SubjectPrincipal凭证
Subject类代表了一个验证实体它可以是用户管理员Web服务设备或者其他的过程该类包含了三中类型的安全信息
身份(Identities)由一个或多个Principal对象表示
公共凭证(Public credentials)例如名称或公共秘钥
私有凭证(Private credentials)例如口令或私有密钥
Principal对象代表了Subject对象的身份它们实现了javasecurityPrincipal和javaioSerializable接口在Subject类中最重要的方法是getName()该方法返回一个身份名称在Subject对象中包含了多个Principal对象因此它可以拥有多个名称由于登录名称身份证号和Email地址都可以作为用户的身份标识可见拥有多个身份名称的情况在实际应用中是非常普遍的情况
在上面提到的凭证并不是一个特定的类或借口它可以是任何对象凭证中可以包含任何特定安全系统需要的验证信息例如标签(ticket)密钥或口令Subject对象中维护着一组特定的私有和公有的凭证这些凭证可以通过getPrivateCredentials()和getPublicCredentials()方法获得这些方法通常在应用程序层中的安全子系统被调用
验证LoginContext
在应用程序层中你可以使用LoginContext对象来验证Subject对象LoginContext对象同时体现了JAAS的动态可插入性(Dynamic Pluggability)因为当你创建一个LoginContext的实例时你需要指定一个配置LoginContext通常从一个文本文件中加载配置信息这些配置信息告诉LoginContext对象在登录时使用哪一个LoginModule对象
下面列出了在LoginContext中经常使用的三个方法
login () 进行登录操作该方法激活了配置中制定的所有LoginModule对象如果成功它将创建一个经过了验证的Subject对象否则抛出LoginException异