class="MsoNormal">安全管理器是一个允许应用程序实现安全策略的类。它允许应用程序在执行一个可能不安全或敏感的操作前确定该操作是什么,以及是否是在允许执行该操作的安全上下文中执行它。应用程序可以允许或不允许该操作。
SecurityManager 类包含了很多名称以单词 check 开头的方法。Java 库中的各种方法在执行某些潜在的敏感操作前可以调用这些方法。对 check 方法的典型调用如下:
???? SecurityManager security = System.getSecurityManager();
???? if (security != null) {
???????? security.checkXXX(argument, ?.?.?.?);
???? }
?
因此,安全管理器通过抛出异常来提供阻止操作完成的机会。如果允许执行该操作,则安全管理器例程只是简单地返回,但如果不允许执行该操作,则抛出一个 SecurityException。该约定的唯一例外是 checkTopLevelWindow,它返回 boolean 值。
当前的安全管理器由 System 类中的 setSecurityManager 方法设置。当前的安全管理器由 getSecurityManager 方法获得。
特殊方法 checkPermission(java.security.Permission)确定是应该允许还是拒绝由指定权限所指示的访问请求。默认的实现调用
?? AccessController.checkPermission(perm);
?
如果允许访问请求,则安静地返回 checkPermission。如果拒绝访问请求,则抛出 SecurityException。
从 Java 2 SDK v1.2 开始,SecurityManager 中其他所有 check 方法的默认实现都是调用 SecurityManager checkPermission 方法来确定调用线程是否具有执行所请求操作的权限。
注意,只带有单个权限参数的 checkPermission 方法总是在当前执行的线程上下文中执行安全检查。有时,应该在给定上下文中进行的安全检查实际上需要在不同 的上下文(例如,在一个辅助线程中)中进行。Java 为这种情况提供了包含有上下文参数的 getSecurityContext方法和 checkPermission方法。getSecurityContext 方法返回当前调用上下文的一个“快照”(默认的实现返回一个 AccessControlContext 对象)。下面是一个示例调用:
?? Object context = null;
?? SecurityManager sm = System.getSecurityManager();
?? if (sm != null) context = sm.getSecurityContext();
?
checkPermission 方法使用一个上下文对象,以及根据该上下文而不是当前执行线程的上下文作出访问决策的权限。因此另一个上下文中的代码可以调用此方法,传递权限和以前保存的上下文对象。下面是一个示例调用,它使用了以前示例中获得的 SecurityManager sm:
?? if (sm != null) sm.checkPermission(permission, context);
?
权限分为以下类别:文件、套接字、网络、安全性、运行时、属性、AWT、反射和可序列化。管理各种权限类别的类是 java.io.FilePermission、java.net.SocketPermission、java.net.NetPermission、java.security.SecurityPermission、java.lang.RuntimePermission、java.util.PropertyPermission、java.awt.AWTPermission、java.lang.reflect.ReflectPermission 和 java.io.SerializablePermission。
除前两个(FilePermission 和 SocketPermission)类以外的所有类都是 java.security.BasicPermission 的子类,而 java.security.BasicPermission 类又是顶级权限类 java.security.Permission 的抽象子类。BasicPermission 定义了所有权限所需的功能,这些功能的名称遵从分层属性命名惯例(例如“exitVM”、“setFactory”、“queuePrintJob”等等)。在名称的末尾可能出现一个星号,前面是“.”或星号,这表示通配符匹配。例如:“a.*”、“*”是有效的,而“*a”或“a*b”是无效的。
FilePermission 和 SocketPermission 是顶级权限类 (java.security.Permission) 的子类。像这些命名语法比 BasicPermission 所用的语法更为复杂的类都直接是 Permission 的子类,而不是 BasicPermission 的子类。例如,对于 java.io.FilePermission 对象而言,权限名就是文件(或目录)的路径名。
某些权限类具有一个“动作”列表,告知允许对象所执行的动作。例如,对于 java.io.FilePermission 对象,动作列表(如“读、写”)指定了允许对指定文件(或指定目录中的文件)执行哪些动作。
其他权限类是“指定的”权限 - 有名称但没有动作列表的类;您也许有指定的权限,也许没有。
注:还有一个暗指所有权限的 java.security.AllPermission 权限。该权限是为了简化系统管理员的工作而存在的,因为管理员可能需要执行很多需要所有(或许多)权限的任务。
当我们运行一个程序,我们可以指定JVM命令?-Djava.security.manager?使SecurityManager运行。
?java -Djava.security.manager <class_name>
这是打开SecurityManager最常见的方式。java.security.manager是一个系统属性,您可以使用System.getProperty(“java.security.manager”)检查该系统属性是否被设置。
在这里,你可能会认为,我们可以使用System.setProperty(“java.security.manager”)打开SecurityManager,但是并不能这么设置。因为先前我们提到,这个系统属性是在当JVM启动时进行检查的。如果我们用程序手动设置该属性,并不能奏效,因为JVM已经启动了,已经过了检查系统属性的步骤了。
现在,如果我们真的想通过我们的程序打开SecurityManager,我们也能做到。 System类中有一个叫 setSecurityManager() 的方法可以做到这一点。这个方法的参数是一个SecurityManager实例。
?SecurityManager sm=new SecurityManager();
System.setSecurityManager(sm);
通过这个,我们可以打开SecurityManager.。如果之后我们想要关闭SecurityManager, 我们该怎么做? 下面的代码能做到吗?
?SecurityManager sm=System.getSecurityManager();
if(sm!=null){
????System.setSecurityManager(null);
}
上面的代码只有你在位于${JDK_HOME}/jre/lib/security目录下或者其他指定目录下的java.policy文件中指定了一个权限才会奏效。 这个权限是:
?permission java.lang.RuntimePermission "setSecurityManager";
上面的一行将被用来允许代码设置SecurityManager
默认加载的策略文件的位置,我们可以从${JDK_HOME}/jre/lib/security目录下的java.security文件中看到:
# The default is to have a single system-wide policy file,
# and a policy file in the user's home directory.
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy
?
也可以在启动的时候指定其他位置的策略文件:
java -Djava.security.manager -Djava.security.policy=someURL SomeApp
JDK8有下面这么多的权限类,每个权限类控制某一方面的相关权限,而每个权限类可能又会有不同的动作,所以控制范围是方方面面的。
?
?
?
?
?
public class SecurityManagerTest { private static class MySecurityManager extends SecurityManager { @Override public void checkRead(String file) { if ("java.policy".contains(file)) { throw new AccessControlException("cannot read file:" + file); } super.checkRead(file); } } public static void main(String[] args) throws FileNotFoundException { //install System.setSecurityManager(new MySecurityManager()); //read InputStream in = new FileInputStream(new File("java.policy")); //uninstall SecurityManager sm = System.getSecurityManager(); if (sm != null) { System.setSecurityManager(null); } } }
?
输出:
Exception in thread "main" java.security.AccessControlException: cannot read file:java.policy
??? at grucee.test.SecurityManagerTest$MySecurityManager.checkRead(SecurityManagerTest.java:17)
??? at java.io.FileInputStream.<init>(FileInputStream.java:121)
??? at grucee.test.SecurityManagerTest.main(SecurityManagerTest.java:29)
?
Java SecurityManager可以让web浏览器在它自己的沙箱中运行applet,从而防止不可信代码访问本地文件系统中的文件、连接applet加载主机之外的主机,等等。同样,SecurityManager可以阻止你的浏览器运行不安全的applet;运行tomcat的时候,使用SecurityManager可以保护你的机器不受恶意Servlet、JSP,甚至是疏忽的错误的影响。
想象一下一个有权限发布JSP页面的人员,由于输出在他们的JSP页面中包括了下面这句:
<% System.exit(1); %>
Tomcat每次执行这个JSP,tomcat都会退出。使用Java SecurityManager是系统管理员保证服务器安全、可靠地另外一种防线。
无论如何,运行SecurityManger比不运行要好很多。
Permission类用来定义tomcat加载的类应该有什么权限。JDK有很多标准的Permission类,并且你可以创建自己的Permission类。Tomcat两种技术都有使用。
这里只是适用于tomcat的标准SecurityManager Permission类的总结:
Tomcat使用了?org.apache.naming.JndiPermission的自定义权限类。该权限控制基于JNDI命名的资源的访问。权限的名称是JNDI名,无动作。可以使用结尾的*来进行模糊匹配,例如
permission? org.apache.naming.JndiPermission? "jndi://localhost/examples/*";
每个部署的Web应用都会动态生成这样的权限实体,以允许读取自己的静态资源,而不允许读取其他的文件(除非显示赋予这些文件的权限)。
而且,Tocat总是动态创建下面的文件权限:
?
permission java.io.FilePermission "** your application context**", "read";
?
permission java.io.FilePermission
? "** application working directory**", "read,write";
permission java.io.FilePermission
? "** application working directory**/-", "read,write,delete";
这里的**your application context”等同于应用程序部署的目录(或WAR文件),**application working directory**是Servlet规范要求的临时目录。
Java SecurityManager实现的安全策略文件配置在$CATALINA_BASE/conf/catalina.policy。该文件完全替换JDK目录中的java.policy文件。catalina.policy文件可以手动编辑,也可以使用Java 1.2及以上自带的policytool工具。
catalina.policy文件中的实体使用标准java.policy文件的格式,如下:
// Example policy file entry
grant [signedBy <signer>,] [codeBase <code source>] {
? permission? <class>? [<name> [, <action list>]];
};
signedBy和codeBase实体在赋予权限的时候是可选的。注释以//开头并至行尾。codeBase以URL的格式,文件URL可以使用${java.home}和${catalina.home}?属性(将来会扩展到JAVA_HOME,CATALINA_HOME以及CATALINA_BASE环境变量)。
$CATALINA_HOME/bin/catalina.sh start -security??? (Unix)
%CATALINA_HOME%\bin\catalina start -security????? (Windows)
?
一旦你配置了catalina.policy文件,Tomcat可以使用”-security”选项启动SecurityManager。