欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

上层应用如何为其所依赖的基础SDK里的静态属性赋值?,为了提高合作商户侧A

来源: javaer 分享于  点击 46803 次 点评:267

上层应用如何为其所依赖的基础SDK里的静态属性赋值?,为了提高合作商户侧A


我们的系统对商户暴露了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 使用场景和技术栈,选择最合适的方案即可确保密钥安全注入。


当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/p/18911285


相关栏目:

用户点评