授权概述
了解 Java 平台如何实现授权的访问控制对于了解我们将在本章中讨论的概念很重要。Java 平台使用访问控制环境(access control context)的概念来确定当前执行线程的权限。从概念上讲,可以将它视作与每个执行线程连接的令牌。在 JAAS 之前,访问控制基于了解当前 Java .class 文件的代码来源或数字签名者的身份。在这种模型下,访问控制是基于了解代码出自于何处。有了 JAAS,我们将模型转了个向。通过将Subject添加到访问控制环境,我们可以开始根据谁正在执行(或要求执行)一段给定代码来授予或拒绝访问权。
在本章中,您将了解 JAAS 的用于控制对敏感代码访问的机制。我们将首先描述授权在 JAAS 中是如何工作的,然后继续更深入地描述授权框架的每个组件。在本章的最后,我们将给出在较大型的运行示例中使用的一些代码样本,它们演示了程序性授权和声明性授权技术。读完本章之后,您应该清楚地知道 JAAS 的认证和授权机制如何一起工作来保护基于 Java 的系统。
访问控制和权限 | 第 2 页(共9 页) |
因为执行线程可以跨越多个具有不同环境特征的模块,所以 Java 平台实现了最小特权这一概念。在属于给定执行线程的整个调用程序栈中,调用栈的成员具有不同特征,用于确定权限的结果是所有这些特征的交集或最小公分母。例如,如果一段调用代码有受限权限(可能由于未对它签名,所以它不可信),但它调用一段信任度较高的代码(可能有一个签名),则降低被调用代码中的权限来匹配较低的信任度。
将包含在访问控制环境中的权限特征与策略文件中的 Java 权限grant语句进行比较,以表明是否允许敏感操作。这是由名为AccessController的 Java 实用程序完成的,它的接口用于通过程序检查特权以及将当前的Subject与活动的访问控制环境相关联。(较旧的 Java 安全性管理器(Java Security Manager)接口已经过时,所以一定要使用AccessController方法。)
将 Subject 绑定到访问控制环境 | 第 3 页(共9 页) |
因为可以在应用程序启动之后认证Subject,所以必须有一个将Subject动态绑定到访问控制环境的方法,以创建一个包含代码权限(从何处装入它以及谁对它进行签名)和用户权限(Subject)环境。为此,我们使用方法Object doAs(Subject subject, PrivilegedAction action)。这个doAs方法调用特别为授权设计的类,该类实现PrivilegedAction接口。
如果不使用线程的当前方法,则可以使用另一种调用方法Object doAsPrivileged(Subject, PrivilegedAction action, AccessControlContext acc)来指定访问控制环境。它的特殊用法是将AccessControlContext设置为空,在doAsPrivileged调用发生时,可以使调用栈短路,并且在PrivilegedAction对象中时,允许增加权限。稍后,当对象返回到调用程序时,将减少权限。本教程稍后将说明这两种技术。
doAs和doAsPrivileged方法形式上都允许抛出PrivilegedActionException。
权限 | 第 4 页(共9 页) |
Java 平台有许多用于控制对系统资源的访问的内置权限。例如:
grant signedBy "Brad", codeBase "http://www.bradrubin.com" {
permission java.io.FilePermission "/tmp/abc", "read";
};
允许由“Brad”签名的并从“http://www.bradrubin.com”装入的代码读取/tmp/abc目录。有关 Java 权限的完整列表,请参阅[url=file:///C:/Documents%20and%20Settings/Administrator.ZTZ/桌面/IBM教程j-sec2/j-sec2/j-sec2-6-2.html]参考资料[/url]。
创建您自己的权限 | 第 5 页(共9 页) |
Java 平台允许您创建自己的权限对象。与正规的权限相似,可以将这些对象放在策略文件中,并在部署时配置它们。为了进行演示,请查看下面的PersonnelPermission。稍后,我们将使用这些代码以允许访问一些敏感的职员信息操作的代码。
import java.security.*;
//
// Implement a user defined permission for access to the personnel //
code for this example public class PersonnelPermission extends
BasicPermission {
public PersonnelPermission(String name) {
super(name);
}
public PersonnelPermission(String name, String action) {
super(name);
}
}
对于上面的权限,您应该注意以下几点:第一,构造器使用用户定义的特权名称(在这个示例中,只有一种名为 access的类型)。另外一个构造器使用名为 action的附加的改进的参数,虽然这里不使用它。对于这个示例,将使用BasicPermission类。如果我们需要更多特性,可以使用Permission类。
策略文件 | 第 6 页(共9 页) |
策略文件是控制对系统资源(包括敏感代码)访问的主要机制。本示例中的策略文件名为 jaas.policy,并且在 Java 命令行中由特性-Djava.security.policy==jaas.policy指定。双等于号(==)表明将替换系统策略文件,而不是添加到系统策略文件权限中。下面是我们正在本教程中使用的 jaas.policy 文件:
grant {
permission javax.security.auth.AuthPermission "createLoginContext";
permission javax.security.auth.AuthPermission "doAs";
permission javax.security.auth.AuthPermission "doAsPrivileged";
permission javax.security.auth.AuthPermission "modifyPrincipals";
permission javax.security.auth.AuthPermission "getSubject"; };
grantprincipal PrincipalImpl "Brad" {
permission PersonnelPermission "access";
};
为了使 JAAS 机制自举,系统必须有某些特权 — 即示例中的前五个。通过这些适当的权限,将访问权PersonnelPermission(用户定义的权限)授予“Brad”主体。
JAAS 主程序示例 | 第 7 页(共9 页) |
下面是这个示例的主应用程序(从命令行调用)。它实例化登录环境、然后登录、尝试执行两个敏感对象(一个对象使用程序性授权,另一个对象使用声明性授权),最后注销。接下来,我们将更深入地研究主程序的两个元素:程序性授权和声明性授权。
import java.security.*;
import javax.security.auth.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
//
// This is the main program in the JAAS Example.It creates a Login
// Context, logs the user in based on the settings in the Login
// Configuration file,and calls two sensitive pieces of code, the
// first using programmatic authorization, and the second using
// declarative authorization.
public class JAASExample {
static LoginContext lc = null;
public static void main( String[] args) {
//
// Create a login context
try {
lc = new LoginContext("JAASExample",
new UsernamePasswordCallbackHandler());
} catch (LoginException le) {
System.out.println( "Login Context Creation Error" );
System.exit(1);
}
//
// Login
try {
lc.login();
} catch (LoginException le) {
System.out.println( "\nOVERALL AUTHENTICATION FAILED\n" );
System.exit(1);
}
System.out.println( "\nOVERALL AUTHENTICATION SUCCEEDED\n" );
System.out.println( lc.getSubject() );
//
// Call the sensitive PayrollAction code, which uses programmatic
// authorization.
try {
Subject.doAs( lc.getSubject(), new PayrollAction() );
} catch (AccessControlException e) {
System.out.println( "Payroll Access DENIED" );
}
//
// Call the sensitive PersonnelAction code, which uses declarative
// authorization.
try {
Subject.doAsPrivileged( lc.getSubject(), new PersonnelAction(), null );
} catch (AccessControlException e) {
System.out.println( "Personnel Access DENIED" );
}
try {
lc.logout();
} catch (LoginException le) {
System.out.println( "Logout FAILED" );
System.exit(1);
}
System.exit(0);
}
}
程序性授权示例 | 第 8 页(共9 页) |
在这个示例中,我们将了解如何编码程序权限决定。PrivilegedAction类由主 JAASExample 程序的doAs方法调用,因此当它输入run方法时,经认证的Subject被绑定到线程上的应用程序环境。
我们从访问控制器检索当前的Subject,遍历任何包含的经认证的Principal,以查找“joeuser”。如果找到他,则可以进行敏感的操作并返回。如果找不到,我们抛出一个AccessControlException。显然,在现实生活中我们应该使用更易于管理且可伸缩的技术,而不是将用户名直接硬编码到应用程序中。
import java.io.*;
import java.security.*;
import javax.security.auth.*;
import javax.security.auth.login.*;
import java.util.*;
//
// This class is a sensitive Payroll function that demonstrates the
// use of programmatic authorization which only allows a subject
// that contains the principal "joeuser" in class PayrollAction
implements PrivilegedAction {
public Object run() {
// Get the passed in subject from the DoAs
AccessControlContext context = AccessController.getContext();
Subject subject = Subject.getSubject(
context );
if (subject == null ) {
throw new AccessControlException("Denied");
}
//
// Iterate through the principal set looking for joeuser.If
// he is not found,
Set principals = subject.getPrincipals();
Iterator iterator = principals.iterator();
while (iterator.hasNext()) {
PrincipalImpl principal = (PrincipalImpl)iterator.next();
if (principal.getName().equals( "joeuser" )) {
System.out.println("joeuser has Payroll access\n");
return new Integer(0);
}
}
throw new AccessControlException("Denied");
}
}
声明性授权示例 | 第 9 页(共9 页) |
在这个示例中,我们将通过使用用户定义的权限PersonnelPermission,来演示如何用策略文件中的权限授予以声明性授权的方式来控制授权检查。我们只询问AccessController是否已经授予这个权限,如果没有授予,它抛出一个AccessControlException,否则,如果已经授予,则保持运行。我们用主 JAASExample 代码中的doAsPrivileged调用和空访问控制环境来调用这个PrivilegedAction,以使调用时调用栈短路。因为在将Subject与doAsPrivileged调用中的环境结合之前,Subject不是环境的一部分,也未经 grant 语句授权,而且还因为使用了“最小特权”和权限的交集,所以这是必需的,否则将不允许提高权限的级别。
import java.io.*;
import java.security.*;
//
// This class is a sensitive Personnel function that demonstrates
// the use of declarative authorization using the user defined
// permission PersonnelPermission, which throws an exception
// if it not granted
class PersonnelAction implements PrivilegedAction {
public Object run() {
AccessController.checkPermission(new PersonnelPermission("access"));
System.out.println( "Subject has Personnel access\n");
return new Integer(0);
}
}