I have a peculiar requirement where I need ensure that only a particular method from one class is allowed to call a public (non-static) method from a second class. Inheritance cannot be used.
One option is to use StackTrace as follows:
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");
}
}
}
ClassB.java
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();
}
}
}
Now the above solution works fine, but due to quality control in code audit I need to come up with another (or rather) better solution. Is there a better way to achieve this?
The right thing to do would be to revisit your requirement. A method that can only be called by certain other code paths is not compatible with public
. The general best practice is to use package-private to prevent external callers, and accept that any code in the package could call the method, but won't because you or your team is auditing it.
Method visibility is ultimately not a secure solution to preventing execution; someone has your .class files and the ability to execute them on a machine, they can do just about anything they want. You shouldn't spend too much time trying to lock down method calls. Instead, document the intent of the method clearly (e.g. "Helper function for methodB()
, please do not use elsewhere.") and trust the people developing with you know what they're doing. You can even give the method a clear name, like dangerousMethodBForInternalUseOnly()
if you really want to beat people over the head about it.
You may also be interested in dependency-injection, which is a design pattern that uses the type system to protect (not prevent) people from executing dangerous code. Here's a couple of talks on Guice, a popular DI framework, that goes into more detail about the concept:
All of that said, as an academic exercise here's one option for restricting method invocation to a fixed number of codepaths - rely on a shared secret. Add an Object secret
field to your locked-down method, and cause the method to fail if the passed secret
does not match a hard-coded value (private static final Object SECRET = new Object()
). You can then use other mechanisms to share the secret only to code paths you allow (e.g. have a static initializer in your locked-down class publish it to classes you explicitly trust).
Obviously this can still be worked-around by a malicious developer, and it's pretty gross, but it would provide some sort of locking behavior assuming you can trust your locked-down class won't be changed without your knowledge.
A way to improve you method is that you don't need to create an exception to get the stacktrace, you can use the thread methods.
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
Also maybe you want to use the class instead of handwriting the package. For example:
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");
}
Apart from that I don't know how you can do it better without changing your logic or using inheritance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With