Java-如何限制从特定方法调用方法

Java-如何限制从特定方法调用方法,java,methods,restrict,Java,Methods,Restrict,我有一个特殊的要求,我需要确保只有一个类中的特定方法才允许从第二个类调用公共(非静态)方法。无法使用继承 一个选项是使用StackTrace,如下所示: ClassA.java package org.rnd.stack; public class ClassA { public void methodA() throws IllegalAccessException { Exception fake = new Exception("FAKE-IGNORE");

我有一个特殊的要求,我需要确保只有一个类中的特定方法才允许从第二个类调用公共(非静态)方法。无法使用继承

一个选项是使用StackTrace,如下所示:

ClassA.java

package org.rnd.stack;


public class ClassA {
    public void methodA() throws IllegalAccessException {
        Exception fake = new Exception("FAKE-IGNORE");
        StackTraceElement[] stack = fake.getStackTrace();
        StackTraceElement st = stack[1];
        if ("org.rnd.stack.ClassB".equals(st.getClassName())
                && "methodB".equals(st.getMethodName())) {
            System.out.println("You are allowed to call");
        } else {
            throw new IllegalAccessException("You are not allowed to call");
        }
    }
}
package org.rnd.stack;

public class ClassB {
    public void methodB() throws IllegalAccessException {
        new ClassA().methodA();
    }

    public void illegalMethod() throws IllegalAccessException {
        new ClassA().methodA();
    }

    public static void main(String[] args) {
        try {
            new ClassB().methodB();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
ClassB.java

package org.rnd.stack;


public class ClassA {
    public void methodA() throws IllegalAccessException {
        Exception fake = new Exception("FAKE-IGNORE");
        StackTraceElement[] stack = fake.getStackTrace();
        StackTraceElement st = stack[1];
        if ("org.rnd.stack.ClassB".equals(st.getClassName())
                && "methodB".equals(st.getMethodName())) {
            System.out.println("You are allowed to call");
        } else {
            throw new IllegalAccessException("You are not allowed to call");
        }
    }
}
package org.rnd.stack;

public class ClassB {
    public void methodB() throws IllegalAccessException {
        new ClassA().methodA();
    }

    public void illegalMethod() throws IllegalAccessException {
        new ClassA().methodA();
    }

    public static void main(String[] args) {
        try {
            new ClassB().methodB();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

现在,上面的解决方案运行良好,但由于代码审计中的质量控制,我需要提出另一个(或者更确切地说)更好的解决方案。有没有更好的方法来实现这一点?

正确的做法是重新审视您的需求。只能由某些其他代码路径调用的方法与
public
不兼容。一般的最佳实践是使用包私有来阻止外部调用方,并接受包中的任何代码都可以调用该方法,但不会,因为您或您的团队正在审核它

方法可见性最终不是防止执行的安全解决方案;有人拥有你的.class文件,并且能够在机器上执行它们,他们可以做任何他们想做的事情。您不应该花费太多时间试图锁定方法调用。相反,请清楚地记录该方法的意图(例如,“methodB()的Helper函数
请不要在其他地方使用”),并信任与您一起开发的人员,您知道他们在做什么。你甚至可以给这个方法一个明确的名字,比如
dangerousMethodBForInternalUseOnly()
,如果你真的想在这个问题上击败人们的话

您可能还对它感兴趣,它是一种使用类型系统来保护(而不是阻止)人们执行危险代码的设计模式。下面是关于Guice(一种流行的DI框架)的几次讨论,将更详细地介绍这个概念:


所有这些都表明,作为一项学术实践,这里有一个选项可以将方法调用限制为固定数量的代码路径——依赖于共享秘密。在锁定的方法中添加
对象机密
字段,如果传递的
机密
与硬编码值不匹配(
私有静态最终对象机密=新对象()
),则会导致该方法失败。然后,您可以使用其他机制将机密仅共享给您允许的代码路径(例如,在锁定的类中有一个静态初始值设定项将其发布到您明确信任的类)


显然,恶意开发人员仍然可以解决这一问题,这是非常糟糕的,但如果您可以信任您锁定的类,并且在您不知情的情况下不会更改,那么它将提供某种锁定行为。

改进方法的一种方法是,您不需要创建异常来获取stacktrace,您可以使用thread方法

StackTraceElement[] stack = Thread.currentThread().getStackTrace();
另外,您可能希望使用类而不是手写包。例如:

if (ClassB.class.getName().equals(st.getClassName())
                && "methodB".equals(st.getMethodName())) {
    System.out.println("You are allowed to call");
} else {
    throw new IllegalAccessException("You are not allowed to call");
}
除此之外,我不知道如何在不改变逻辑或使用继承的情况下做得更好

  • 将调用者作为参数传递,并检查调用者是否是
    instanceof
    必需的类-多线程解决方案,不能通过reflection绕过
  • 获取线程堆栈转储并检查顶部条目-奇怪、沉重但可能
  • 创建代理-但这将是解决方案1的过度变化

  • 您可以通过使用class
    class
    方法
    getEnclosuringMethod()
    来满足此要求。这就是它的工作原理():

    如果该类对象表示方法中的本地类或匿名类,则返回表示基础类的直接封闭方法的方法对象

  • methodA()
    的签名应更改为接受
    Class
    对象作为参数

    public void methodA(Class c) { }
    
  • ClassB
    中的合法方法应该创建一个匿名类对象,并将其类作为参数传递给
    methodA()

  • 然后
    methodA()
    应该检查封闭方法的类是否确实是
    methodB()
    来自
    ClassB

    public void methodA(Class c) throws IllegalAccessException, NoSuchMethodException {
        if (c.getEnclosingMethod().equals(ClassB.class.getMethod("methodB"))) {
            System.out.println("You are allowed to call");
        } else {
            throw new IllegalAccessException("You are not allowed to call");
        }
    }
    
  • 缺点:

    • 每次调用
      methodB()
      时都必须实例化一个新对象。这可能会变得昂贵,这取决于你做了多少次。相反,您可以在
      methodB()
      中创建一个本地类,这样就不会产生对象创建开销

      public void methodB() throws IllegalAccessException, NoSuchMethodException {
          class Local {};
          new ClassA().methodA(Local.class);
      }
      
    • 您需要处理
      NoSuchMethodException
      并在
      methodB()
      名称更改时更改代码

    • 有权访问代码的人仍然可以修改
      methodB()
      ,将匿名对象类返回给另一个方法,并使用它从那里调用
      methodA()
      所以这不是一个完美的解决方案,但对于您的用例来说可能已经足够了

    这是一个非常奇怪的要求。你为什么需要它?正是。。。通常对于这种奇怪的需求,您应该考虑更改逻辑以完全避免这种情况。更改public方法以获取对象引用,并使用
    instanceof
    检查对象引用类型可能不那么脆弱。或者编写一些特殊的单元测试。但是@Neilos的建议更合理。用一个包含非法方法的新C扩展B怎么样?嗯。。。这有点尴尬,但我现在的处境是,架构师不允许我使用继承来解决这个问题,忘记改变逻辑(就在第八宗罪旁边…)。“改变设计方式”的空间很小,另一方面,质量控制有一个自动审核员,它扫描StackTrace的使用情况,作为“潜在的JVM内存泄漏”。相信我,当我这么说的时候,自动审核员被尊重,就好像它是某种神圣的神一样。是的,这个要求不仅奇怪而且完全无法解释@安东尼奥斯感谢为什么投票失败?如果你不告诉我,我怎么解决呢?我喜欢这个主意,但这和wh不一样吗