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

我们的系统对商户暴露了RestAPI,供合作商户以API的形式接入。为了提高合作商户侧API接入的开发效率,我编写了一个SDK。

下面 ClientApiUtils是这个SDK一个工具类,封装了API数据加解密、API数字签名的工具方法。这些工具方法都是静态方法。在这个 ClientApiUtils中,有两个静态field,platformPrivateKeyplatformPublicKey,分别是我们系统的数字签名RSA公私钥。

package com.zfquan.clientapi.sdk.common;

public final class ClientApiUtils {
    private static String platformPrivateKey;
    private static String platformPublicKey;

    //  加密 及 签名  --- client端请求我方api时调用
    public static void encryptThenSign(LevyRequestBase requestBase, String encryptKey, String privateKey) {...}

    //  加密 及 签名  --- 我方主动发起通知请求时调用
    public static void encryptThenSignUsingPlatformSignKey(LevyRequestBase requestBase, String encryptKey){...}

    //  验签及解密--- 我方接收到client端请求后的验签
    public static void verifySignThenDecrypt(LevyRequestBase requestBase, String publicKey, String encryptKey){...}
    ...
}

platformPrivateKeyplatformPublicKey这两个field的值,需要被依赖的上层应用来赋值。

那么,要实现 SDK 中的 ClientApiUtils 工具类能够从上层应用获取 platformPrivateKeyplatformPublicKey 的值,以下是几种推荐的实现方案:


方案 1:静态初始化方法(推荐)

实现方式
@Slf4j
public final class ClientApiUtils {
    private static String platformPrivateKey;
    private static String platformPublicKey;
    
    // 初始化方法(需上层应用显式调用)
    public static void initPlatformKey(String platformKey, KeyType keyType) {
        if (keyType == KeyType.PUBLIC_KEY)
            ClientApiUtils.platformPublicKey = platformKey;
        else
            ClientApiUtils.platformPrivateKey = platformKey;
    }
    
    ...
}
上层应用调用
package com.zfquan.config;

@Configuration
@Data
public class CommonConfig {
    @Value("${platform.api.privateKey:}")
    private String platformPrivateKey;
    @Value("${platform.api.publicKey:}")
    private String platformPublicKey;

    // 在应用启动时初始化
    @PostConstruct
    public void init() {
        ClientApiUtils.initPlatformKey(platformPrivateKey, KeyType.PRIVATE_KEY);
        ClientApiUtils.initPlatformKey(platformPublicKey, KeyType.PUBLIC_KEY);
    }
优点
  • 简单直接,无第三方依赖
  • 明确控制初始化时机
缺点
  • 需手动调用,依赖开发者主动初始化

方案 2:配置文件注入

实现方式
@Slf4j
public final class ClientApiUtils {
    private static String platformPrivateKey;
    private static String platformPublicKey;
    
    static {
        try (InputStream is = ClientApiUtils.class.getResourceAsStream("/sdk-config.properties")) {
            Properties props = new Properties();
            props.load(is);
            platformPrivateKey = props.getProperty("platform.private.key");
            platformPublicKey = props.getProperty("platform.public.key");
        } catch (IOException e) {
            throw new RuntimeException("SDK配置加载失败", e);
        }
    }
}
配置文件 (sdk-config.properties)
platform.private.key=MIIEvQ...
platform.public.key=MIIBI...
优点
  • 配置与代码分离
  • 支持热更新(需重载配置)
缺点
  • 需约定配置文件路径
  • 不适用于动态密钥场景

方案 3:SPI 机制(面向接口)

步骤 1:定义密钥提供接口
package com.zfquan.clientapi.sdk;

public interface KeyProvider {
    String getPrivateKey();
    String getPublicKey();
}
步骤 2:工具类通过 SPI 获取密钥
@Slf4j
public final class ClientApiUtils {
    private static final KeyProvider keyProvider;
    
    static {
        ServiceLoader<KeyProvider> loader = ServiceLoader.load(KeyProvider.class);
        keyProvider = loader.findFirst().orElseThrow(() -> 
            new IllegalStateException("未实现KeyProvider接口")
        );
    }
    
    public static void verifySign(...) {
        String pubKey = keyProvider.getPublicKey();
        // ...
    }
}
步骤 3:上层应用实现接口
// 在META-INF/services/com.zfquan.clientapi.sdk.KeyProvider文件中注册
public class AppKeyProvider implements KeyProvider {
    @Override
    public String getPrivateKey() { return "MIIEvQ..."; }
    
    @Override
    public String getPublicKey() { return "MIIBI..."; }
}
**步骤 4:文件注册

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

优点
  • 完全解耦,符合开闭原则
  • 支持多实现动态选择
缺点
  • 实现稍复杂
  • 需熟悉 SPI 机制

方案 4:Spring Aware 集成(适合 Spring 项目)

步骤 1:创建 Spring 上下文感知类
@Component
public class SdkKeyInjector implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        KeyConfig config = ctx.getBean(KeyConfig.class);
        ClientApiUtils.init(config.getPrivateKey(), config.getPublicKey());
    }
}
步骤 2:上层应用配置密钥
@Configuration
public class KeyConfig {
    @Value("${platform.private.key}") 
    private String privateKey;
    
    @Value("${platform.public.key}")
    private String publicKey;
    
    // Getter省略
}
优点
  • 天然集成 Spring 生态
  • 支持 Spring 配置方式(properties/YAML)
缺点
  • 强依赖 Spring 框架
  • 非 Spring 项目不可用

方案 5:动态回调机制

实现方式
@Slf4j
public final class ClientApiUtils {
    private static KeyLoader keyLoader;
    
    // 注册密钥加载器
    public static void registerKeyLoader(KeyLoader loader) {
        keyLoader = loader;
    }
    
    public static void verifySign(...) {
        String pubKey = keyLoader.getPublicKey();
        // ...
    }
    
    public interface KeyLoader {
        String getPrivateKey();
        String getPublicKey();
    }
}
上层应用实现
// 应用启动时注册
@PostConstruct
public void setupSDK() {
    ClientApiUtils.registerKeyLoader(new KeyLoader() {
        @Override
        public String getPrivateKey() { return "MIIEvQ..."; }
        
        @Override
        public String getPublicKey() { return "MIIBI..."; }
    });
}
优点
  • 灵活支持动态密钥
  • 无框架依赖
缺点
  • 需手动实现回调

各方案对比

方案适用场景复杂度灵活性框架依赖
静态初始化方法简单应用★☆☆☆☆★★☆☆☆
配置文件注入配置驱动型应用★★☆☆☆★★★☆☆
SPI 机制需要扩展性的SDK★★★★☆★★★★★
Spring Aware 集成Spring Boot 项目★★★☆☆★★★★☆强依赖
动态回调机制需要运行时动态获取密钥★★★☆☆★★★★★
方案推荐
  • 通用 SDK → 选择 SPI 机制(方案3),提供标准扩展接口
  • Spring 项目 → 选择 Spring Aware 集成(方案4),无缝融入生态
  • 快速实现 → 选择 静态初始化方法(方案1),简单高效

秘钥存储安全建议

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值