(转载)Spring源码--关于AccessController.doPrivileged
来源:互联网 发布:电子书编辑软件下载 编辑:程序博客网 时间:2024/06/06 12:39
原文链接
在Spring里发现一段代码,位置在DefaultListableBeanFactory -> preInstantiateSingletons()方法里。
如下:
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { public Boolean run() { return ((SmartFactoryBean<?>) factory).isEagerInit(); } }, getAccessControlContext());}
对于这个AccessController.doPrivileged,表示完全不懂是怎么回事。现在具体分析一下。
场景
当一个代码片段加载到现有的系统中,如果不对这个代码加以访问控制,那么它极有可能对当前系统安全造成破坏。比如这段代码会偷偷读取磁盘上的所有文件,然后保存到另外一台服务器上,造成机密信息泄露。
最常见的就是applet程序,它会被限制在一个沙箱里运行。或者导入其他jar包到系统,而这个jar包中的程序也有可能是恶意的。
所以,如果对这些代码进行权限控制是理所当然的。
安全管理器SecurityManager
作用是用于检查代码执行权限。其实日常的很多API都涉及到安全管理器,它的工作原理一般是:
1. 请求Java API
2. Java API使用安全管理器判断许可权限
3. 通过则顺序执行,否则抛出一个Exception
经典的FileInputStream,会调用SecurityManager的checkRead方法,用于检查是否有读取某个文件的权限。
public FileInputStream(File file) throws FileNotFoundException { String name = (file != null ? file.getPath() : null); SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(name); } if (name == null) { throw new NullPointerException(); } if (file.isInvalid()) { throw new FileNotFoundException("Invalid file path"); } fd = new FileDescriptor(); fd.incrementAndGetUseCount(); this.path = name; open(name);}
SecurityManager里的方法列表如下,
checkAccept(String, int)checkAccess(Thread)checkAccess(ThreadGroup)checkAwtEventQueueAccess()checkConnect(String, int)checkConnect(String, int, Object)checkCreateClassLoader()checkDelete(String)checkExec(String)checkExit(int)checkLink(String)checkListen(int)checkMemberAccess(Class<?>, int)checkMulticast(InetAddress)checkMulticast(InetAddress, byte)checkPackageAccess(String)checkPackageDefinition(String)checkPermission(Permission)checkPermission(Permission, Object)checkPrintJobAccess()checkPropertiesAccess()checkPropertyAccess(String)checkRead(FileDescriptor)checkRead(String)checkRead(String, Object)checkSecurityAccess(String)checkSetFactory()checkSystemClipboardAccess()checkTopLevelWindow(Object)checkWrite(FileDescriptor)checkWrite(String)
都是check方法,分别囊括了文件的读写删除和执行、网络的连接和监听、线程的访问、以及其他包括打印机剪贴板等系统功能。而这些check代码也基本横叉到了所有的核心Java API上。
访问控制器AccessController
上面的SecurityManager都是基于AccessController实现的,还是继续上面的FileInputStream。
security.checkRead()方法的细节,
public void checkRead(String file) { checkPermission(new FilePermission(file, SecurityConstants.FILE_READ_ACTION));}
checkPermission()的细节,
public void checkPermission(Permission perm) { java.security.AccessController.checkPermission(perm);}
可以看出,SecurityManager会调用AccessController里的方法。
概念
需要理解4个概念:代码源、权限、策略和保护域。
代码源CodeSource
CodeSource就是一个简单的类,用来声明从哪里加载类。
权限Permission
Permission类是AccessController处理的基本实体。Permission类本身是抽象的,它的一个实例代表一个具体的权限。权限有两个作用,一个是允许Java API完成对某些资源的访问。另一个是可以为自定义权限提供一个范本。权限包含了权限类型、权限名和一组权限操作。具体可以看看BasicPermission类的代码。典型的也可以参看FilePermission的实现。
策略Policy
策略是一组权限的总称,用于确定权限应该用于哪些代码源。话说回来,代码源标识了类的来源,权限声明了具体的限制。那么策略就是将二者联系起来,策略类Policy主要的方法就是getPermissions(CodeSource)和refresh()方法。Policy类在老版本中是abstract的,且这两个方法也是。在jdk1.8中已经不再有abstract方法。这两个方法也都有了默认实现。
在JVM中,任何情况下只能安装一个策略类的实例。安装策略类可以通过Policy.setPolicy()方法来进行,也可以通过java.security文件里的policy.provider=sun.security.provider.PolicyFile来进行。jdk1.6以后,Policy引入了PolicySpi,后续的扩展基于SPI进行。
保护域ProtectionDomain
保护域可以理解为代码源和相应权限的一个组合。表示指派给一个代码源的所有权限。看概念,感觉和策略很像,其实策略要比这个大一点,保护域是一个代码源的一组权限,而策略是所有的代码源对应的所有的权限的关系。
JVM中的每一个类都一定属于且仅属于一个保护域,这由ClassLoader在define class的时候决定。但不是每个ClassLoader都有相应的保护域,核心Java API的ClassLoader就没有指定保护域,可以理解为属于系统保护域。
使用方法
这是一个无法实例化的类——仅仅可以使用其static方法。
checkPermission
AccessController最重要的方法就是checkPermission()方法,作用是基于已经安装的Policy对象,能否得到某个权限。
doPrivileged
还有一个方法,就是doPrivileged()方法,获取特权,用于绕过权限检查。
参考
Java安全——安全管理器、访问控制器和类装载器
Java 授权内幕