防止API中的System.exit()

时间:2011-03-23 05:42:18

标签: java securitymanager

我正在使用第三方库,如果遇到异常则执行System.exit()。我正在使用jar中的API。无论如何,我可以阻止System.exit()调用,因为它导致我的应用程序关闭?由于许多其他许可问题,我在删除System.exit()后无法反编译并重新编译jar。我曾经在stackoverflow中遇到了[我不记得的其他问题]的答案,我们可以使用Java中的SecurityManager来做这样的事情。

4 个答案:

答案 0 :(得分:32)

这里有一篇博文,

http://jroller.com/ethdsy/entry/disabling_system_exit

基本上它安装了一个安全管理器,它使用here的代码禁用System.exit(),

  private static class ExitTrappedException extends SecurityException { }

  private static void forbidSystemExitCall() {
    final SecurityManager securityManager = new SecurityManager() {
      public void checkPermission( Permission permission ) {
        if( "exitVM".equals( permission.getName() ) ) {
          throw new ExitTrappedException() ;
        }
      }
    } ;
    System.setSecurityManager( securityManager ) ;
  }

  private static void enableSystemExitCall() {
    System.setSecurityManager( null ) ;
  }

修改Max在下面的评论中指出

  

从Java 6开始,权限名称实际上是“exitVM。”+ status,例如“exitVM.0”。

但是,权限exitVM.*会引用所有退出状态,exitVM会保留为exitVM.*的简写,因此上述代码仍然有效(请参阅documentation for RuntimePermission )。

答案 1 :(得分:6)

请参阅我对How to avoid JFrame EXIT_ON_CLOSE operation to exit the entire application?的回复。

编辑1:链接的来源。演示如何使用SecurityManager来阻止System.exit(n)

import java.awt.*;
import java.awt.event.*;
import java.security.Permission;

/** NoExit demonstrates how to prevent 'child'
applications from ending the VM with a call
to System.exit(0).
@author Andrew Thompson */
public class NoExit extends Frame implements ActionListener {

  Button frameLaunch = new Button("Frame"),
     exitLaunch = new Button("Exit");

  /** Stores a reference to the original security manager. */
  ExitManager sm;

  public NoExit() {
     super("Launcher Application");

     sm = new ExitManager( System.getSecurityManager() );
     System.setSecurityManager(sm);

     setLayout(new GridLayout(0,1));

     frameLaunch.addActionListener(this);
     exitLaunch.addActionListener(this);

     add( frameLaunch );
     add( exitLaunch );

     pack();
     setSize( getPreferredSize() );
  }

  public void actionPerformed(ActionEvent ae) {
     if ( ae.getSource()==frameLaunch ) {
        TargetFrame tf = new TargetFrame();
     } else {
        // change back to the standard SM that allows exit.
        System.setSecurityManager(
           sm.getOriginalSecurityManager() );
        // exit the VM when *we* want
        System.exit(0);
     }
  }

  public static void main(String[] args) {
     NoExit ne = new NoExit();
     ne.setVisible(true);
  }
}

/** This example frame attempts to System.exit(0)
on closing, we must prevent it from doing so. */
class TargetFrame extends Frame {
  static int x=0, y=0;

  TargetFrame() {
     super("Close Me!");
     add(new Label("Hi!"));

     addWindowListener( new WindowAdapter() {
        public void windowClosing(WindowEvent we) {
           System.out.println("Bye!");
           System.exit(0);
        }
     });

     pack();
     setSize( getPreferredSize() );
     setLocation(++x*10,++y*10);
     setVisible(true);
  }
}

/** Our custom ExitManager does not allow the VM
to exit, but does allow itself to be replaced by
the original security manager.
@author Andrew Thompson */
class ExitManager extends SecurityManager {

  SecurityManager original;

  ExitManager(SecurityManager original) {
     this.original = original;
  }

  /** Deny permission to exit the VM. */
   public void checkExit(int status) {
     throw( new SecurityException() );
  }

  /** Allow this security manager to be replaced,
  if fact, allow pretty much everything. */
  public void checkPermission(Permission perm) {
  }

  public SecurityManager getOriginalSecurityManager() {
     return original;
  }
}

答案 2 :(得分:3)

使用SecurityManager禁止System.exit()调用并不完美,至少有两个原因:

  1. 在启用和未启用SecurityManager的情况下运行的Java应用程序非常不同。这就是它最终需要关闭的原因,但无法使用System.setSecurityManager(null)完成。此调用将导致另一个安全权限检查,这将不可避免地失败,因为应用程序代码(SecurityManager子类)位于调用堆栈的顶部。

  2. 所有Java应用程序都是多线程的,其他线程可以在forbidSystemExitCall()和enableSystemExitCall()之间执行各种操作。其中一些可以通过安全权限检查来保护,这将由于与上述相同的原因而失败。如果覆盖checkExit()而不是[更多通用] checkPermission(),它将覆盖大多数情况。

  3. 解决此问题的唯一方法(即我知道)是将所有特权授予SecurityManager子类。它很可能要求它由一个单独的类加载器加载,例如bootstrap(null)类加载器。

答案 3 :(得分:3)

之前的代码示例部分正确,但我发现它最终阻止了代码对文件的访问。为了解决这个问题,我用不同的方式编写了我的SecurityManager:

public class MySecurityManager extends SecurityManager {

private SecurityManager baseSecurityManager;

public MySecurityManager(SecurityManager baseSecurityManager) {
    this.baseSecurityManager = baseSecurityManager;
}

@Override
public void checkPermission(Permission permission) {
    if (permission.getName().startsWith("exitVM")) {
        throw new SecurityException("System exit not allowed");
    }
    if (baseSecurityManager != null) {
        baseSecurityManager.checkPermission(permission);
    } else {
        return;
    }
}

}

就我而言,我需要阻止第三方库终止虚拟机。但也有一些Grails测试调用System.exit。因此,我编写了我的代码,以便它只在调用第三方库之前立即激活自定义安全管理器(不是常见事件),然后立即恢复原始安全管理器(如果有的话)。

这有点难看。理想情况下,我宁愿删除System.exit代码,但我无法访问第三方库的源代码。