1. 概述
在本教程中,我们将介绍默认情况下禁用的Java内置安全基础设施。具体来说,我们将检查其主要组件、扩展点和配置。
2. SecurityManager实战
这可能令人惊讶,但默认的SecurityManager设置不允许许多标准操作:
System.setSecurityManager(new SecurityManager());
new URL("http://www.google.com").openConnection().connect();
在这里,我们以编程方式使用默认设置启用安全监督并尝试连接到google.com。
然后我们得到以下异常:
java.security.AccessControlException: access denied ("java.net.SocketPermission"
"www.google.com:80" "connect,resolve")
标准库中还有许多其他用例-例如,读取系统属性、读取环境变量、打开文件、反射和更改语言环境,仅举几例。
3. 用例
这种安全基础设施从Java 1.0开始就可用,在那个时代,小程序(嵌入到浏览器中的Java应用程序)非常普遍。自然地,有必要限制他们对系统资源的访问。
如今,小程序已经过时了。但是,当存在第三方代码在受保护环境中执行的情况时,安全实施仍然是一个实际概念。
例如,假设我们有一个Tomcat实例,第三方客户端可以在其中托管他们的Web应用程序。我们不想让他们执行像System.exit()这样的操作,因为这会影响其他应用程序,甚至可能影响整个环境。
4. 设计
4.1 SecurityManager
内置安全基础结构中的主要组件之一是java.lang SecurityManager,它有几个checkXxx方法,比如checkConnect,它授权我们在上面的测试中尝试连接到谷歌。它们都委托给checkPermission(java.security.Permission)方法。
4.2 Permission
java.security.Permission实例代表授权请求。标准JDK类为所有潜在的危险操作(如读/写文件、打开套接字等)创建它们,并将它们交给SecurityManager以获得适当的授权。
4.3 Configuration
我们以特殊的策略格式定义权限,这些权限采用授予条目的形式:
grant codeBase "file:$/*" {
permission java.security.AllPermission;
};
上面的codeBase规则是可选的,我们可以在其中完全不指定任何字段或使用signedBy(与密钥库中的相应证书集成)或principal(通过javax.security.auth.Subject附加到当前线程的java.security.Principal),我们可以使用这些规则的任意组合。
默认情况下,JVM加载位于<java.home>/lib/security/java.policy的公共系统策略文件。如果我们在<user.home>/.java.policy中定义了任何用户本地策略,JVM会将其附加到系统策略。
也可以通过命令行指定策略文件:-Djava.security.policy=/my/policy-file,这样我们就可以将策略附加到先前加载的系统和用户策略中。
有一个特殊的语法用于替换所有系统和用户策略(如果有的话)-双等号:-Djava.security.policy==/my/policy-file
5. 例子
让我们定义一个自定义权限:
public class CustomPermission extends BasicPermission {
public CustomPermission(String name) {
super(name);
}
public CustomPermission(String name, String actions) {
super(name, actions);
}
}
以及应该保护的共享服务:
public class Service {
public static final String OPERATION = "my-operation";
public void operation() {
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
securityManager.checkPermission(new CustomPermission(OPERATION));
}
System.out.println("Operation is executed");
}
}
如果我们尝试在启用安全管理器的情况下运行它,则会抛出异常:
java.security.AccessControlException: access denied
("cn.tuyucheng.taketoday.security.manager.CustomPermission" "my-operation")
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
at java.security.AccessController.checkPermission(AccessController.java:884)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
at cn.tuyucheng.taketoday.security.manager.Service.operation(Service.java:10)
我们可以创建包含以下内容的<user.home>/.java.policy文件并尝试重新运行应用程序:
grant codeBase "file:<our-code-source>" {
permission cn.tuyucheng.taketoday.security.manager.CustomPermission "my-operation";
};
现在应该正常运行。
6. 总结
在本文中,我们检查了内置JDK安全系统的组织方式以及我们如何扩展它。尽管目标用例相对较少,但了解它还是有好处的。
与往常一样,本教程的完整源代码可在GitHub上获得。