术语中英对照
缩写 | 全拼 | 中文解释 |
---|---|---|
JAAS | Java Authentication and Authorization Service | Java身份验证和授权服务 |
PAM | Pluggable Authentication Module | 可插拔身份验证模块 |
概览
JAAS 代表Java身份验证和授权服务,它被用于两个目的:
- 用户认证,可靠且安全地确定当前是谁在执行Java代码
- 用户授权,确保他们拥有执行操作所需的访问控制权限。
JAAS实现了标准可插拔身份验证模块(PAM)框架的Java版本
传统的java提供了基于codesource-based 访问控制(访问控制基于代码的来源和谁签名了该代码)。但是,它缺乏根据谁运行代码来附加强制访问控制的能力。JAAS提供了一个框架,通过这种支持来增强Java安全体系结构。
JAAS身份验证是以可插入的方式执行的。这允许应用程序独立于底层身份验证技术,可以在应用程序下插入新的或更新的身份验证技术,而不需要修改应用程序本身。应用程序通过实例化LoginContext对象来启用身份验证过程,而LoginContext
对象又引用Configuration
或 LoginModule
,用于执行身份验证。典型的LoginModules
可能会提示输入和验证用户名和密码
执行代码的用户或服务经过身份验证后,JAAS授权组件将与核心Java SE访问控制模型一起工作,以保护对敏感资源的访问。访问控制决策既基于执行代码的CodeSource,也基于运行代码的用户或服务,后者由Subject
对象表示,如果身份验证成功,则使用相关Principals
和credentials 的LoginModule
更新Subject。
核心类和接口
与jaas相关的核心类和接口可以分为三类:Common, Authentication, and Authorization.
Common 类
公共类是由JAAS身份验证和授权组件共享的类。关键的JAAS类是javax.security.auth.Subject
,它表示单个实体(如人)的相关信息的分组。它包含实体的Principals
、公共credentials
和私有credentials
Subject
要授权访问资源,应用程序首先需要对请求的源进行身份验证。JAAS框架定义了表示请求源的术语subject
。主体可以是任何实体,例如人或服务。一旦对主题进行了身份验证,则使用javax.security.auth.Subject
由关联的标识或Principals填充。一个subject
可能有多个principal
。例如,一个人可能有一个name Principal(“John Doe”)
和一个SSN Principal(“123-45-6789”)
subject
还可以拥有与安全性相关的属性,这些属性称为credentials
凭据,敏感的credentials
比如私钥使用Set<Object> privCredentials
表示,共享非敏感credentials
比如公钥使用Set<Object> pubCredentials
表示。访问和修改不同的凭据集需要不同的权限(如下所述):
subject
是使用这些构造函数创建的:
public Subject();
public Subject(boolean readOnly, Set principals,
Set pubCredentials, Set privCredentials);
第一个构造函数创建了一个principal
和credential
为空(非空)的subject
。第二个构造函数使用指定的principals
和credentials
创建subject
。它还有一个布尔参数,可用于使subject
为只读。在只读subject
中,principals
和credentials
是不可变的。
应用程序编写者不必实例化subject
。如果应用程序实例化了一个LoginContext
,而没有将Subject
传递给LoginContext
构造函数,那么LoginContext
实例化了一个新的空Subject
如果没有在实例化Subject使其处于只读状态,则可以通过调用以下方法将其设置为只读:
public boolean isReadOnly();
要获取与subject
相关的principals
,有两种方法:
public Set getPrincipals();
public Set getPrincipals(Class c);
第一个方法返回subject
中的所有principals
,而第二个方法只返回指定类c
的实例或类c
的子类的实例化的principals
。如果subject
没有任何关联的principals
,则返回一个空集合。
获取subject
的公有凭证和私有凭证的方式和获取主体的方式类似
public Set getPublicCredentials();
public Set getPublicCredentials(Class c);
public Set getPrivateCredentials();
public Set getPrivateCredentials(Class c);
要修改或操作subject
的principals
、pubCredentials
或privCredentials
,调用者使用java.util.Set
中定义的方法。下面的示例演示了这一点:
Subject subject;
Principal principal;
Object credential;
. . .
// add a Principal and credential to the Subject
subject.getPrincipals().add(principal);
subject.getPublicCredentials().add(credential);
主题可以与AccessControlContext
关联,下面的方法返回与指定AccessControlContext
关联的subject
,如果没有与指定AccessControlContext
关联的subject
,则返回null
。
public static Subject getSubject(final AccessControlContext acc);
作为特定Subject
执行操作的doAs
方法
可以调用下列静态方法来执行作为特定Subject
的操作:
public static Object
doAs(final Subject subject,
final java.security.PrivilegedAction action);
public static Object
doAs(final Subject subject,
final java.security.PrivilegedExceptionAction action)
throws java.security.PrivilegedActionException;
这两个方法首先将指定的Subject
与当前线程的AccessControlContext
关联,然后执行操作。这实现了将动作作为Subject
运行的效果。第一个方法可以抛出运行时异常,但正常执行时它必须从其action参数的run方法返回一个对象,第二个方法的行为类似,只是它可以从PrivilegedExceptionAction
的 run方法中抛出一个已检查的异常
Subject.doAs Example
假设名为“Bob”的人已经通过LoginContext
(请参阅LoginContext
)进行了身份验证,因此,Subject
中填充了com.ibm.security.Principal
类的Principal
。这个Principal
的名字叫BOB,还假设已经安装了SecurityManager,并且访问控制策略中存在以下内容:
// grant "BOB" permission to read the file "foo.txt"
grant Principal com.ibm.security.Principal "BOB" {
permission java.io.FilePermission "foo.txt", "read";
};
下面是示例应用程序代码
class ExampleAction implements java.security.PrivilegedAction {
public Object run() {
java.io.File f = new java.io.File("foo.txt");
// the following call invokes a security check
if (f.exists()) {
System.out.println("File foo.txt exists");
}
return null;
}
}
public class Example1 {
public static void main(String[] args) {
// Authenticate the subject, "BOB".
// This process is described in the
// LoginContext class.
Subject bob;
// Set bob to the Subject created during the
// authentication process
// perform "ExampleAction" as "BOB"
Subject.doAs(bob, new ExampleAction());
}
}
在执行过程中,ExampleAction调用f.exists()时将遇到安全检查。但是,由于ExampleAction作为“BOB”运行,并且策略(上面)将必要的文件权限授予“BOB”,因此ExampleAction将通过安全检查。如果策略中的grant语句被更改(例如,添加不正确的CodeBase 或将主体更改为“MOE”),则会抛出SecurityException。
doAsPrivileged methods
public static Object doAsPrivileged(
final Subject subject,
final java.security.PrivilegedAction action,
final java.security.AccessControlContext acc);
public static Object doAsPrivileged(
final Subject subject,
final java.security.PrivilegedExceptionAction action,
final java.security.AccessControlContext acc)
throws java.security.PrivilegedActionException;
doAs与doAsPrivileged
doasprivilege
方法的行为与doAs
方法完全相同,只是它需要通过传入一个AccessControlContext
而不是将提供的subject
与当前线程的AccessControlContext
关联。通过这种方式,可以通过与当前上下文不同的AccessControlContext
来控制操作。
AccessControlContext
包含自实例化AccessControlContext
以来执行的所有代码的信息,包括代码位置和策略授予代码的权限。为了使访问控制检查成功,策略必须为AccessControlContext
引用的每个代码项授予所需的权限。
如果提供给doasprivilege
的AccessControlContext
为null
,则操作不受单独的AccessControlContext
的限制。比如在服务器环境中。服务器可以对多个传入请求进行身份验证,并为每个请求执行单独的doAs
操作。要启动每个doAs
操作,并且不受当前服务器AccessControlContext
的限制,服务器可以调用doAsPrivileged
并传入null
的AccessControlContext
。
Principals
正如前面提到的,旦对主题进行了身份验证,则使用javax.security.auth.Subject
由关联的标识或Principals填充。一个subject
可能有多个principal
。例如,一个人可能有一个name Principal(“John Doe”)
和一个SSN Principal(“123-45-6789”)
。主体必须实现java.security.Principal
和java.io.Serializable
的接口
Credentials
除了关联的主体外,主题还可以拥有与安全性相关的属性,这些属性称为凭据。凭据可能包含用于向新服务验证主题的信息。这些凭证包括密码、Kerberos票据和公钥证书。凭据还可能包含仅使主体能够执行某些活动的数据。例如,加密密钥表示使主体能够签名或加密数据的凭据。公共凭证类和私有凭证类不是核心JAAS类库的一部分。因此,任何类都可以表示凭证
:公共凭证类和私有凭证类不是核心JAAS类库的一部分。然而,开发人员可以选择让他们的凭据类实现两个与凭据相关的接口:Refreshable
和Destroyable
。
Refreshable
javax.security.auth.Refreshable接口提供了凭据刷新自身的功能。例如,具有特定时间限制生命周期的凭据可以实现此接口,以允许调用者在其有效刷新刷新。该接口有两种抽象方法:
boolean isCurrent();
此方法确定凭据是当前的还是有效的
void refresh() throws RefreshFailedException;
此方法更新或扩展凭据的有效性
Destroyable
javax.security.auth.Destroyable接口提供了销毁凭据内内容的功能。该接口有两个抽象方法
boolean isDestroyed();
确定凭据是否已被销毁
void destroy() throws DestroyFailedException;
销毁并清除与此凭据关联的信息。对该凭证上的某些方法的后续调用将导致抛出IllegalStateException
Authentication 类和接口
认证是验证subject
身份的过程,必须以安全的方式执行;否则,犯罪者可能会冒充他人来访问系统。身份验证通常涉及到subject
展示某种形式的证据来证明其身份。这些证据可能是只有受试者可能知道或拥有的信息(例如密码或指纹),也可能是只有受试者能够生成的信息(例如使用私钥签名的数据)。
要对subject
(用户或服务)进行身份验证,需要执行以下步骤:
- 应用程序实例化
LoginContext
-
LoginContext
查询一个Configuration
,以加载为该应用程序配置的所有LoginModules
。 - 应用程序调用
LoginContext
的login
方法 -
login
方法调用所有加载的LoginModules
。每个LoginModule
都尝试对subject
进行身份验证,成功后,LoginModules
将相关principals
和credentials
与表示正在验证的主题的Subject
对象关联。 -
LoginContext
将身份验证状态返回给应用程序 - 如果身份验证成功,应用程序将从
LoginContext
获取主题。
LoginContext
javax.security.auth.login.LoginContext
类提供了用于对subject
进行身份验证的基本方法,并提供了一种独立于底层身份验证技术开发应用程序的方法。LoginContext
查询配置,以确定为特定应用程序配置的身份验证服务(或LoginModule
)。因此,可以在应用程序下插入不同的LoginModules
,而不需要对应用程序本身进行任何修改。
LoginContext
提供了四个构造函数可供选择:
public LoginContext(String name) throws LoginException;
public LoginContext(String name, Subject subject) throws LoginException;
public LoginContext(String name, CallbackHandler callbackHandler)
throws LoginException
public LoginContext(String name, Subject subject,
CallbackHandler callbackHandler) throws LoginException
所有构造函数都共享一个公共参数:name
。LoginContext
使用这个参数作为登录配置的索引,以确定为实例化LoginContext
的应用程序配置了哪些LoginModule
不接受Subject作为输入参数的构造函数实例化一个新Subject。
实际身份验证通过调用以下方法进行:
public void login() throws LoginException;
当调用login时,将调用所有配置的loginmodule来执行身份验证。如果认证成功,可以使用以下方法检索Subject(现在可以保存主体、公共凭证和私有凭证):
public Subject getSubject();
要注销Subject
并删除其经过身份验证的主体和凭据,提供以下方法:
public void logout() throws LoginException;
下面的代码示例演示了验证和注销主题所需的调用:
// let the LoginContext instantiate a new Subject
LoginContext lc = new LoginContext("entryFoo");
try {
// authenticate the Subject
lc.login();
System.out.println("authentication successful");
// get the authenticated Subject
Subject subject = lc.getSubject();
...
// all finished -- logout
lc.logout();
} catch (LoginException le) {
System.err.println("authentication unsuccessful: " +
le.getMessage());
}
LoginModule
LoginModule
接口使开发人员能够实现可以在应用程序中插入的各种身份验证技术。例如,一种类型的LoginModule
可以执行基于用户名/密码的身份验证形式。其他LoginModule
可以与硬件设备(如智能卡或生物识别设备)来进行认证
CallbackHandler
在某些情况下,LoginModule
必须与用户通信才能获得身份验证信息javax.security.auth.callback.CallbackHandler就是用于此目的。应用程序实现CallbackHandler
接口并将其传递给LoginContext
,后者将其直接转发给底层LoginModule
LoginModule
使用CallbackHandler
收集用户的输入(如密码或智能卡密码)或向用户提供信息(如状态信息),通过允许应用程序指定CallbackHandler
,底层LoginModule
可以实现不同的应用程序与用户交互方式。例如,GUI应用程序的CallbackHandler实现可能会显示一个窗口来请求用户输入。另一方面,非gui工具的CallbackHandler
实现可能只是直接从命令行提示用户输入。
CallbackHandler
是一个接口,有一个方法可以实现:
void handle(Callback[] callbacks)
throws java.io.IOException, UnsupportedCallbackException;
LoginModule
向CallbackHandler
的handle
方法传递一系列适当的回调函数,例如用户名的NameCallback
和密码的PasswordCallback
, CallbackHandler
执行请求的用户交互并在回调函数中设置适当的值。例如,要处理NameCallback
, CallbackHandler
可能会提示输入名称,然后获取输入name
,并调用NameCallback
的setName
方法来存储name
。
Callback
javax.security.auth.callback
包包含回调接口和几个实现。LoginModules可以将一个回调数组直接传递给CallbackHandler
的handle
方法。