java 安全管理器详解(2)
2017-09-16 20:21
225 查看
一、运行时代码权限检测
由一个名为 java.lang.SecurityManager 的类负责监督类是否越权。在默认情况下,不会进行权限检测。可通过两种方式开启权限检测:
在启动时传递给 JVM 的、名为 java.security.manager 的环境变量【-Djava.security.manager-Djava.security.policy=[策略文件路径]
】
动态设置SecurityManager (下面会举例说明这种方式)
开启权限检测后,任何代码都可以找到 SecurityManager 并调用它相应的 check 方法来检测是否越权。如果权限没有授予,那么将抛出一个 java.security.AccessControlException.
注:
在java1.1的时代, SecurityManager 通过其内部逻辑负责管理所有权限。 因此,任何需要权限检测的应用程序都必须实现并安装一个 SecurityManager。
Java 2 平台安全体系结构通过引入一个名为 AccessController 的新类使这一切变得简单了,并更具有可扩展性。这个类的目的与 SecurityManager 是一样的,即它负责做出访问决定。当然, 为了向后兼容性保留了 SecurityManager 类,但是其更新的实现委派给了底层的 AccessController。还是需要实现并安装
二、JDK核心代码的权限控制
JDK核心底层代码同样需要进行权限控制,对于不同的操作定义了具体的权限类,涉及内容包括:文件、套接字、网络、安全性、运行时、属性、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 的抽象子类。
三、自定义java类加载器,并通过类加载器授予指定类权限
1.简单介绍类加载器简单说,类加载器就是根据指定全限定名称将class文件加载到JVM内存,转为Class对象。如果站在JVM的角度来看,只存在两种类加载器:
启动类加载器(Bootstrap ClassLoader):由C++语言实现(针对HotSpot),负责将存放在\lib目录或-Xbootclasspath参数指定的路径中的类库加载到内存中。
其他类加载器:由Java语言实现,继承自抽象类ClassLoader。如:
扩展类加载器(Extension ClassLoader):负责加载\lib\ext目录或java.ext.dirs系统变量指定的路径中的所有类库。
应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
2.类加载的顺序
通过观察 java.lang.ClassLoader.loadClass(String name)源码来了解类加载顺序
protected Class<?> loadClass(String name, boolean resolve ) throws ClassNotFoundException { synchronized (getClassLoadingLock(name )) { // First, check if the class has already been loaded Class c = findLoadedClass( name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent. loadClass( name, false ); } else { c = findBootstrapClassOrNull(name ); } } catch (ClassNotFoundException e ) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass( name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime( t1 - t0 ); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom( t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve ) { resolveClass( c); } return c ; } }
类加载大致顺序如下(双亲委派模型):
首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。(findLoadedClass )
如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载
如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。
观察 findClass方法
protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
在上面介绍的类加载顺序中, loadClass在父加载器无法加载类的时候(未重写findClass方法),就会调用我们自定义的类加载器中的findeClass函数
3.自定义类加载器,并授予类加载器权限
继承java.lang.ClassLoader
重写findClass方法
public class MyClassLoader extends ClassLoader{ private String path; public MyClassLoader(String path) { this .path = path ; } @Override protected Class<?> findClass(String name ) throws ClassNotFoundException { byte [] b = null; try { b = getbyte( new File(path )); } catch (Exception e ) { e.printStackTrace(); } PermissionCollection pc = new Permissions(); Permission p = new FilePermission("d:/demo.txt" ,"write" ); pc.add( p); ProtectionDomain defaultDomain = new ProtectionDomain( new CodeSource( null, (Certificate[]) null ), pc, this , null ); return defineClass( null, b, 0 ,b .length , defaultDomain); } private byte [] getbyte(File file ) throws Exception{ FileInputStream in =new FileInputStream( file); byte [] b = new byte[1024]; ByteArrayOutputStream out= new ByteArrayOutputStream(); int len =0; while ((len =in .read(b ))!=-1){ out.write( b, 0, len); } out.close(); b= out.toByteArray(); return b ; } } public class Test { public void hello() { System. out .println("hello world" ); } } public class App { public static void main(String[] args) throws Exception { MyClassLoader mcl = new MyClassLoader("D:/eclipse-workspaces/test/Test.class" ); Class clazz = mcl .loadClass( "Test"); Object obj = clazz.newInstance(); Method helloMethod = clazz .getDeclaredMethod( "hello", null) ; helloMethod .invoke( obj, null) ; System. out .println(clazz .getProtectionDomain ()); } }
在上面的打印结果中,能清晰的看到权限:
由类加载器MyClassLoader加载的类,对于d:/demo.txt只有写的权限。
4.对象权限验证
我们通过 java.lang.SecurityManager 类来进行权限验证
运行时别忘了打开权限检测,默认是不开启的:
运行之后提示main方法没有创建类加载器的权限,这个可以暂时不理会,在下面会讲
Exception in thread "main" java.security.AccessControlException : access denied ("java.lang.RuntimePermission" "createClassLoader") at java.security.AccessControlContext.checkPermission( AccessControlContext.java:366) at java.security.AccessController.checkPermission( AccessController.java:560) at java.lang.SecurityManager.checkPermission( SecurityManager.java:549) at java.lang.SecurityManager.checkCreateClassLoader( SecurityManager.java:611) at java.lang.ClassLoader.checkCreateClassLoader( ClassLoader.java:273) at java.lang.ClassLoader.<init>(ClassLoader.java:334 ) at com.classloaddemo.MyClassLoader.<init>( MyClassLoader.java:18) at com.classloaddemo.App.main( App.java:10 )
正常运行的情况下,应该会输出一下内容:
因为没有对d:/demo.text读的权限,所以会抛出AccessControlException
java.security.AccessControlException : access denied ("java.io.FilePermission" "D:\eclipse-workspaces\test\Test.class" "read") at java.security.AccessControlContext.checkPermission( AccessControlContext.java:366) at java.security.AccessController.checkPermission( AccessController.java:560) at java.lang.SecurityManager.checkPermission( SecurityManager.java:549) at java.lang.SecurityManager.checkRead(SecurityManager.java:888 ) at java.io.FileInputStream.<init>(FileInputStream.java:131 ) at com.classloaddemo.MyClassLoader.getbyte( MyClassLoader.java:44) at com.classloaddemo.MyClassLoader.findClass( MyClassLoader.java:26) at java.lang.ClassLoader.loadClass(ClassLoader.java:423 ) at java.lang.ClassLoader.loadClass(ClassLoader.java:356 ) at com.classloaddemo.App.main( App.java:12 )
四、策略文件的使用
除了上面一种通过类加载的方式授予权限,java还可以通过配置文件进行授权,整个应用共同使用一个策略文件。策略文件的语法可参考:http://blog.csdn.net/hudashi/article/details/7069764
上面遇到过的异常:
Exception in thread "main" java.security.AccessControlException : access denied ("java.lang.RuntimePermission" "createClassLoader") at java.security.AccessControlContext.checkPermission( AccessControlContext.java:366) at java.security.AccessController.checkPermission( AccessController.java:560) at java.lang.SecurityManager.checkPermission( SecurityManager.java:549) at java.lang.SecurityManager.checkCreateClassLoader( SecurityManager.java:611) at java.lang.ClassLoader.checkCreateClassLoader( ClassLoader.java:273) at java.lang.ClassLoader.<init>(ClassLoader.java:334 ) at com.classloaddemo.MyClassLoader.<init>( MyClassLoader.java:18) at com.classloaddemo.App.main( App.java:10 )
我们可以指定策略文件,并在策略文件中增加以下配置来解决:
permission java.lang.RuntimePermission "createClassLoader";
在启动时指定策略文件的位置:
相关文章推荐
- java 安全管理器详解(1)
- SQL Server数据库安全管理机制详解
- 详解Java String字符串对象的创建及管理(2)
- Java日志管理:Logger.getLogger()和LogFactory.getLog()的区别(详解Log4j)
- java 安全沙箱模型详解
- Java日志管理:Logger.getLogger()和LogFactory.getLog()的区别(详解Log4j)
- SQLServer数据库安全管理机制详解
- Java日志管理:Logger.getLogger()和LogFactory.getLog()的区别(详解Log4j)
- Hadoop安全管理-java安全介绍
- Java日志管理:Logger.getLogger()和LogFactory.getLog()的区别(详解Log4j)
- Java日志管理:Logger.getLogger()和LogFactory.getLog()的区别(详解Log4j)
- Java日志管理:Logger.getLogger()和LogFactory.getLog()的区别(详解Log4j)
- java 安全退出线程详解
- Java日志管理:Logger.getLogger()和LogFactory.getLog()的区别(详解Log4j)
- java 安全管理概念,代码签名和安全设置
- java 安全沙箱模型详解(转)
- Java日志管理:Logger.getLogger()和LogFactory.getLog()的区别(详解Log4j)
- java安全机制和数字证书管理
- SQLServer数据库安全管理机制详解