找回密码
 立即注册
首页 业界区 安全 上层应用如何为其所依赖的基础SDK里的静态属性赋值? ...

上层应用如何为其所依赖的基础SDK里的静态属性赋值?

伯斌 4 天前
我们的系统对商户暴露了RestAPI,供合作商户以API的形式接入。为了提高合作商户侧API接入的开发效率,我编写了一个SDK。
下面 ClientApiUtils是这个SDK一个工具类,封装了API数据加解密、API数字签名的工具方法。这些工具方法都是静态方法。在这个 ClientApiUtils中,有两个静态field,platformPrivateKey和platformPublicKey,分别是我们系统的数字签名RSA公私钥。
  1. package com.zfquan.clientapi.sdk.common;
  2. public final class ClientApiUtils {
  3.     private static String platformPrivateKey;
  4.     private static String platformPublicKey;
  5.     //  加密 及 签名  --- client端请求我方api时调用
  6.     public static void encryptThenSign(LevyRequestBase requestBase, String encryptKey, String privateKey) {...}
  7.     //  加密 及 签名  --- 我方主动发起通知请求时调用
  8.     public static void encryptThenSignUsingPlatformSignKey(LevyRequestBase requestBase, String encryptKey){...}
  9.     //  验签及解密--- 我方接收到client端请求后的验签
  10.     public static void verifySignThenDecrypt(LevyRequestBase requestBase, String publicKey, String encryptKey){...}
  11.     ...
  12. }
复制代码
platformPrivateKey和platformPublicKey这两个field的值,需要被依赖的上层应用来赋值。
那么,要实现 SDK 中的 ClientApiUtils 工具类能够从上层应用获取 platformPrivateKey 和 platformPublicKey 的值,以下是几种推荐的实现方案:
方案 1:静态初始化方法(推荐)

实现方式
  1. @Slf4j
  2. public final class ClientApiUtils {
  3.     private static String platformPrivateKey;
  4.     private static String platformPublicKey;
  5.    
  6.     // 初始化方法(需上层应用显式调用)
  7.     public static void initPlatformKey(String platformKey, KeyType keyType) {
  8.         if (keyType == KeyType.PUBLIC_KEY)
  9.             ClientApiUtils.platformPublicKey = platformKey;
  10.         else
  11.             ClientApiUtils.platformPrivateKey = platformKey;
  12.     }
  13.    
  14.     ...
  15. }
复制代码
上层应用调用
  1. package com.zfquan.config;
  2. @Configuration
  3. @Data
  4. public class CommonConfig {
  5.     @Value("${platform.api.privateKey:}")
  6.     private String platformPrivateKey;
  7.     @Value("${platform.api.publicKey:}")
  8.     private String platformPublicKey;
  9.     // 在应用启动时初始化
  10.     @PostConstruct
  11.     public void init() {
  12.         ClientApiUtils.initPlatformKey(platformPrivateKey, KeyType.PRIVATE_KEY);
  13.         ClientApiUtils.initPlatformKey(platformPublicKey, KeyType.PUBLIC_KEY);
  14.     }
复制代码
优点


  • 简单直接,无第三方依赖
  • 明确控制初始化时机
缺点


  • 需手动调用,依赖开发者主动初始化
方案 2:配置文件注入

实现方式
  1. @Slf4j
  2. public final class ClientApiUtils {
  3.     private static String platformPrivateKey;
  4.     private static String platformPublicKey;
  5.    
  6.     static {
  7.         try (InputStream is = ClientApiUtils.class.getResourceAsStream("/sdk-config.properties")) {
  8.             Properties props = new Properties();
  9.             props.load(is);
  10.             platformPrivateKey = props.getProperty("platform.private.key");
  11.             platformPublicKey = props.getProperty("platform.public.key");
  12.         } catch (IOException e) {
  13.             throw new RuntimeException("SDK配置加载失败", e);
  14.         }
  15.     }
  16. }
复制代码
配置文件 (sdk-config.properties)
  1. platform.private.key=MIIEvQ...
  2. platform.public.key=MIIBI...
复制代码
优点


  • 配置与代码分离
  • 支持热更新(需重载配置)
缺点


  • 需约定配置文件路径
  • 不适用于动态密钥场景
方案 3:SPI 机制(面向接口)

步骤 1:定义密钥提供接口
  1. package com.zfquan.clientapi.sdk;
  2. public interface KeyProvider {
  3.     String getPrivateKey();
  4.     String getPublicKey();
  5. }
复制代码
步骤 2:工具类通过 SPI 获取密钥
  1. @Slf4j
  2. public final class ClientApiUtils {
  3.     private static final KeyProvider keyProvider;
  4.    
  5.     static {
  6.         ServiceLoader<KeyProvider> loader = ServiceLoader.load(KeyProvider.class);
  7.         keyProvider = loader.findFirst().orElseThrow(() ->
  8.             new IllegalStateException("未实现KeyProvider接口")
  9.         );
  10.     }
  11.    
  12.     public static void verifySign(...) {
  13.         String pubKey = keyProvider.getPublicKey();
  14.         // ...
  15.     }
  16. }
复制代码
步骤 3:上层应用实现接口
  1. // 在META-INF/services/com.zfquan.clientapi.sdk.KeyProvider文件中注册
  2. public class AppKeyProvider implements KeyProvider {
  3.     @Override
  4.     public String getPrivateKey() { return "MIIEvQ..."; }
  5.    
  6.     @Override
  7.     public String getPublicKey() { return "MIIBI..."; }
  8. }
复制代码
**步骤 4:文件注册

创建文件
src/main/resources/META-INF/services/com.zfquan.clientapi.sdk.KeyProvider,文件内容就是步骤3中AppKeyProvider类的全限定名称。
优点


  • 完全解耦,符合开闭原则
  • 支持多实现动态选择
缺点


  • 实现稍复杂
  • 需熟悉 SPI 机制
方案 4:Spring Aware 集成(适合 Spring 项目)

步骤 1:创建 Spring 上下文感知类
  1. @Component
  2. public class SdkKeyInjector implements ApplicationContextAware {
  3.     @Override
  4.     public void setApplicationContext(ApplicationContext ctx) {
  5.         KeyConfig config = ctx.getBean(KeyConfig.class);
  6.         ClientApiUtils.init(config.getPrivateKey(), config.getPublicKey());
  7.     }
  8. }
复制代码
步骤 2:上层应用配置密钥
  1. @Configuration
  2. public class KeyConfig {
  3.     @Value("${platform.private.key}")
  4.     private String privateKey;
  5.    
  6.     @Value("${platform.public.key}")
  7.     private String publicKey;
  8.    
  9.     // Getter省略
  10. }
复制代码
优点


  • 天然集成 Spring 生态
  • 支持 Spring 配置方式(properties/YAML)
缺点


  • 强依赖 Spring 框架
  • 非 Spring 项目不可用
方案 5:动态回调机制

实现方式
  1. @Slf4j
  2. public final class ClientApiUtils {
  3.     private static KeyLoader keyLoader;
  4.    
  5.     // 注册密钥加载器
  6.     public static void registerKeyLoader(KeyLoader loader) {
  7.         keyLoader = loader;
  8.     }
  9.    
  10.     public static void verifySign(...) {
  11.         String pubKey = keyLoader.getPublicKey();
  12.         // ...
  13.     }
  14.    
  15.     public interface KeyLoader {
  16.         String getPrivateKey();
  17.         String getPublicKey();
  18.     }
  19. }
复制代码
上层应用实现
  1. // 应用启动时注册
  2. @PostConstruct
  3. public void setupSDK() {
  4.     ClientApiUtils.registerKeyLoader(new KeyLoader() {
  5.         @Override
  6.         public String getPrivateKey() { return "MIIEvQ..."; }
  7.         
  8.         @Override
  9.         public String getPublicKey() { return "MIIBI..."; }
  10.     });
  11. }
复制代码
优点


  • 灵活支持动态密钥
  • 无框架依赖
缺点


  • 需手动实现回调



各方案对比

方案适用场景复杂度灵活性框架依赖静态初始化方法简单应用★☆☆☆☆★★☆☆☆无配置文件注入配置驱动型应用★★☆☆☆★★★☆☆无SPI 机制需要扩展性的SDK★★★★☆★★★★★无Spring Aware 集成Spring Boot 项目★★★☆☆★★★★☆强依赖动态回调机制需要运行时动态获取密钥★★★☆☆★★★★★无方案推荐


  • 通用 SDK → 选择 SPI 机制(方案3),提供标准扩展接口
  • Spring 项目 → 选择 Spring Aware 集成(方案4),无缝融入生态
  • 快速实现 → 选择 静态初始化方法(方案1),简单高效



秘钥存储安全建议


  • 密钥存储
    1. // 避免硬编码,使用安全存储
    2. private String privateKey = System.getenv("PLATFORM_PRIVATE_KEY");
    复制代码
  • 访问控制
    1. // 限制密钥访问权限
    2. SecurityManager manager = System.getSecurityManager();
    3. if (manager != null) manager.checkPermission(new SDKKeyPermission());
    复制代码
  • 密钥轮换
    1. // SPI实现支持动态更新
    2. public class DynamicKeyProvider implements KeyProvider {
    3.     public String getPublicKey() {
    4.         return KeyVault.getCurrentKey(); // 从密钥管理系统获取
    5.     }
    6. }
    复制代码



以上,商户可根据 SDK 使用场景和技术栈,选择最合适的方案即可确保密钥安全注入。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册