如何防止客户端在 Android 库中看到内部私有 类?
How to prevent client from seeing internal private classes in Android library ?
我有一个包含多个包的库-
让我们说
套餐一;
包 b;
内包a我有publica_class
在包 b 我有 public b_class
a_class 使用 b_class.
我需要从中生成一个库,但我不想让客户端看到 b_class。
我知道的唯一解决方案是将我精美易懂的包扁平化为单个包,并使用 b_class 的默认包访问权限。
还有另一种方法吗?也许使用接口或某种形式的设计模式??
如果我没理解错的话,您是在询问是否要在不公开部分来源的情况下发布您的库供第 3 方使用?如果是这种情况,您可以使用 proguard,这会混淆您的库。默认情况下,所有内容都是 excluded/obfuscated,除非您指定要排除的内容 obfuscated/excluded。
如果您想在客户端根本无法访问代码的情况下分发[部分]代码,这意味着客户端也将无法执行它。 :-O
因此,您只有一个选择:将代码的敏感部分放入 public 服务器 并分发代理来访问它,以便您的代码将保留并执行到您的服务器中,客户端仍然可以通过代理执行它,但无需直接访问它。
您可以使用 servlet、web 服务、RMI 对象或简单的 TCP 服务器,具体取决于代码的复杂程度。
这是我能想到的最安全的方法,但它也值得付出代价:除了使您的系统复杂化之外,它还会为每个远程引入 网络延迟操作,这可能很重要,具体取决于性能要求。此外,您应该确保服务器本身的安全,以避免黑客入侵。如果您已经拥有可以利用的服务器,这可能是一个很好的解决方案。
如果你拒绝将代码移动到一个单独的、受控的服务器上,你所能做的就是在尝试使用你的 API 时 阻碍客户端程序员 .让我们开始将好的实践应用到您的设计中:
- 让您的包裹保持现在的样子。
每 class 你想要 "hide":
- 设为非public。
- 将其 public API 提取到新的 public 界面:
public interface MyInterface {...}
- 创建一个 public 工厂 class 以获取该接口类型的对象。
public class MyFactory
{
public MyInterface createObject();
}
到目前为止,您的包已经松散耦合,并且实现 classes 现在是私有的(正如您已经说过的良好做法所宣扬的那样)。尽管如此,它们仍然可以通过接口和工厂获得。
那么,如何避免 "stranger" 客户端执行您的私人 APIs?接下来是一个创造性的、有点复杂但有效的解决方案,基于 阻碍 客户端程序员:
修改您的工厂classes:向每个工厂方法添加一个新参数:
public class MyFactory
{
public MyInterface createObject(Macguffin parameter);
}
那么,什么是Macguffin
?这是您必须在应用程序中定义的新接口,至少有一个方法:
public interface Macguffin
{
public String dummyMethod();
}
但是不提供此接口的任何可用实现。在你的代码的每个地方你需要提供一个 Macguffin
对象,通过 anonymous class:
创建它
MyFactory.getObject(new Macguffin(){
public String dummyMethod(){
return "x";
}
});
或者,甚至更高级,通过一个动态代理对象,所以即使客户端程序员敢,也不会找到此实现的“.class”文件反编译代码。
你从中得到了什么?基本上是 劝阻 程序员不要使用需要未知、未记录、无法理解的对象的工厂。工厂 classes 应该只关心不要接收空对象,并调用虚拟方法并检查 return 值它也不为空(或者,如果你想要更高的安全级别,添加未记录的秘密密钥规则)。
所以此解决方案依赖于您的 API 的 微妙混淆 ,以阻止客户端程序员直接使用它。 Macguffin接口及其方法的名字越隐晦越好。
I need to generate a library from this , but I do not want the Client to see b_class. The only solution I know of is to flatten my beautifully understandable packages to single package and to use default package access for b_class. Is there another way to do so ?
是的,使 b_class
包私有(默认访问)并通过反射实例化它以便在 a_class
中使用。
既然你知道完整的 class 名称,反射加载 class:
Class<?> clz = Class.forName("b.b_class")
找到您要调用的构造函数:
Constructor<?> con = clz.getDeclaredConstructor();
通过使其可访问来允许自己调用构造函数:
con.setAccessible(true);
调用构造函数来获取您的 b_class
实例:
Object o = con.newInstance();
万岁,现在你有了 b_class
的实例。但是,您不能在 Object
的实例上调用 b_class
的方法,因此您有两个选择:
- 使用反射来调用
b_class
的方法(不是很有趣,但足够简单,如果你只有几个参数很少的方法可能没问题)。
- 让
b_class
实现一个您不介意客户端看到的接口,并将 b_class
的实例投射到该接口(从字里行间我怀疑您可能已经有了这样的接口?)。
你肯定会想要选择选项 2 来减少你的痛苦,除非它让你再次回到原点(用你不想向客户端公开的类型污染命名空间)。
为了完整披露,请注意两点:
1) 使用反射与直接实例化和调用相比有一个(小的)开销。如果你投射到一个接口,你只需支付实例化的反射成本。在任何情况下,除非您在紧密循环中进行数十万次调用,否则这可能不是问题。
2) 没有什么可以阻止坚定的客户找出 class 的名字并做同样的事情,但如果我正确理解你的动机,你只是想公开一个干净的 API,所以这不是真正的担心。
使用 Kotlin 时,您可以为您的库使用 internal
修饰符 类。
我有一个包含多个包的库-
让我们说
套餐一;
包 b;
内包a我有publica_class
在包 b 我有 public b_class
a_class 使用 b_class.
我需要从中生成一个库,但我不想让客户端看到 b_class。
我知道的唯一解决方案是将我精美易懂的包扁平化为单个包,并使用 b_class 的默认包访问权限。 还有另一种方法吗?也许使用接口或某种形式的设计模式??
如果我没理解错的话,您是在询问是否要在不公开部分来源的情况下发布您的库供第 3 方使用?如果是这种情况,您可以使用 proguard,这会混淆您的库。默认情况下,所有内容都是 excluded/obfuscated,除非您指定要排除的内容 obfuscated/excluded。
如果您想在客户端根本无法访问代码的情况下分发[部分]代码,这意味着客户端也将无法执行它。 :-O
因此,您只有一个选择:将代码的敏感部分放入 public 服务器 并分发代理来访问它,以便您的代码将保留并执行到您的服务器中,客户端仍然可以通过代理执行它,但无需直接访问它。
您可以使用 servlet、web 服务、RMI 对象或简单的 TCP 服务器,具体取决于代码的复杂程度。
这是我能想到的最安全的方法,但它也值得付出代价:除了使您的系统复杂化之外,它还会为每个远程引入 网络延迟操作,这可能很重要,具体取决于性能要求。此外,您应该确保服务器本身的安全,以避免黑客入侵。如果您已经拥有可以利用的服务器,这可能是一个很好的解决方案。
如果你拒绝将代码移动到一个单独的、受控的服务器上,你所能做的就是在尝试使用你的 API 时 阻碍客户端程序员 .让我们开始将好的实践应用到您的设计中:
- 让您的包裹保持现在的样子。
每 class 你想要 "hide":
- 设为非public。
- 将其 public API 提取到新的 public 界面:
public interface MyInterface {...}
- 创建一个 public 工厂 class 以获取该接口类型的对象。
public class MyFactory { public MyInterface createObject(); }
到目前为止,您的包已经松散耦合,并且实现 classes 现在是私有的(正如您已经说过的良好做法所宣扬的那样)。尽管如此,它们仍然可以通过接口和工厂获得。
那么,如何避免 "stranger" 客户端执行您的私人 APIs?接下来是一个创造性的、有点复杂但有效的解决方案,基于 阻碍 客户端程序员:
修改您的工厂classes:向每个工厂方法添加一个新参数:
public class MyFactory
{
public MyInterface createObject(Macguffin parameter);
}
那么,什么是Macguffin
?这是您必须在应用程序中定义的新接口,至少有一个方法:
public interface Macguffin
{
public String dummyMethod();
}
但是不提供此接口的任何可用实现。在你的代码的每个地方你需要提供一个 Macguffin
对象,通过 anonymous class:
MyFactory.getObject(new Macguffin(){
public String dummyMethod(){
return "x";
}
});
或者,甚至更高级,通过一个动态代理对象,所以即使客户端程序员敢,也不会找到此实现的“.class”文件反编译代码。
你从中得到了什么?基本上是 劝阻 程序员不要使用需要未知、未记录、无法理解的对象的工厂。工厂 classes 应该只关心不要接收空对象,并调用虚拟方法并检查 return 值它也不为空(或者,如果你想要更高的安全级别,添加未记录的秘密密钥规则)。
所以此解决方案依赖于您的 API 的 微妙混淆 ,以阻止客户端程序员直接使用它。 Macguffin接口及其方法的名字越隐晦越好。
I need to generate a library from this , but I do not want the Client to see b_class. The only solution I know of is to flatten my beautifully understandable packages to single package and to use default package access for b_class. Is there another way to do so ?
是的,使 b_class
包私有(默认访问)并通过反射实例化它以便在 a_class
中使用。
既然你知道完整的 class 名称,反射加载 class:
Class<?> clz = Class.forName("b.b_class")
找到您要调用的构造函数:
Constructor<?> con = clz.getDeclaredConstructor();
通过使其可访问来允许自己调用构造函数:
con.setAccessible(true);
调用构造函数来获取您的 b_class
实例:
Object o = con.newInstance();
万岁,现在你有了 b_class
的实例。但是,您不能在 Object
的实例上调用 b_class
的方法,因此您有两个选择:
- 使用反射来调用
b_class
的方法(不是很有趣,但足够简单,如果你只有几个参数很少的方法可能没问题)。 - 让
b_class
实现一个您不介意客户端看到的接口,并将b_class
的实例投射到该接口(从字里行间我怀疑您可能已经有了这样的接口?)。
你肯定会想要选择选项 2 来减少你的痛苦,除非它让你再次回到原点(用你不想向客户端公开的类型污染命名空间)。
为了完整披露,请注意两点:
1) 使用反射与直接实例化和调用相比有一个(小的)开销。如果你投射到一个接口,你只需支付实例化的反射成本。在任何情况下,除非您在紧密循环中进行数十万次调用,否则这可能不是问题。
2) 没有什么可以阻止坚定的客户找出 class 的名字并做同样的事情,但如果我正确理解你的动机,你只是想公开一个干净的 API,所以这不是真正的担心。
使用 Kotlin 时,您可以为您的库使用 internal
修饰符 类。