Java后端对接美团外卖霸王餐回调:基于HTTPS双向认证与证书轮转

Java后端对接美团外卖霸王餐回调:基于HTTPS双向认证与证书轮转

一、场景痛点:双向认证+证书90天过期
美团霸王餐回调接口 https://open-api.meituan.com/bawangcan/callback 强制 HTTPS双向认证。平台方提供:

  1. server.crt 有效期90天
  2. 我方需上传 client.crt + client.key
  3. 过期未更新直接报 SSLHandshakeException
    要求:零停机 证书自动轮转,且兼容老证书在途请求。

二、总体架构:SpringBoot + Undertow + 动态SSLContext

┌--------------┐     ┌--------------┐
│  美团网关     │-----▶│  负载均衡     │-----▶│  应用集群     │
└--------------┘     └--------------┘     └--------------┘
   双向TLS              四层转发           动态SSLContext
  • 证书统一由 ACM 拉取,本地加密缓存
  • 每12h检测一次,新证书落地即 热替换
  • 老证书持有 CloseableHttpClient 用完即弃,无连接撕裂

三、证书仓库:本地JKS + KMS解密

// juwatech.cn.ssl.CertHolder
@Data
public class CertHolder {
    private X509Certificate clientCert;
    private PrivateKey privateKey;
    private X509Certificate[] serverCa;
    private Instant expireTime;
}

KMS解密后写内存,不落磁盘:

byte[] encryptedP12 = Files.readAllBytes(Paths.get("/secrets/client.p12.enc"));
byte[] plainP12 = KMSUtil.decrypt(encryptedP12);
KeyStore store = KeyStore.getInstance("PKCS12");
store.load(new ByteArrayInputStream(plainP12), pwd);

在这里插入图片描述

四、动态SSLContext工厂

package juwatech.cn.ssl;

public final class DynamicSslContextFactory {
    private static final Map<String, SSLContext> CACHE = new ConcurrentHashMap<>();

    public static SSLContext get(String version) {
        return CACHE.computeIfAbsent(version, v -> build(v));
    }

    private static SSLContext build(String v) {
        CertHolder holder = CertLoader.load(v);
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(KeyStoreUtil.createKeyStore(holder), pwd);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(KeyStoreUtil.createTrustStore(holder));
        SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        return ctx;
    }

    public static void reload(String version) {
        CACHE.put(version, build(version));
    }
}

五、Undertow热替换Connector
SpringBoot 默认 Tomcat 不支持运行时替换 SSLHostConfig,改用 Undertow:

@Bean
public UndertowServletWebServerFactory undertowFactory() {
    UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
    factory.addBuilderCustomizers(builder -> {
        SSLContext newCtx = DynamicSslContextFactory.get("v2");
        builder.addHttpsListener(8443, "0.0.0.0", newCtx);
    });
    return factory;
}

证书更新后,向容器发 SIGUSR1 触发重新绑定:

kill -USR1 $PID

六、HttpClient双向认证调用

// juwatech.cn.open.HttpClientFactory
public static CloseableHttpClient createTwoWayClient(String certVersion) {
    SSLContext ctx = DynamicSslContextFactory.get(certVersion);
    SSLConnectionSocketFactory ssl = new SSLConnectionSocketFactory(
            ctx, NoopHostnameVerifier.INSTANCE);
    Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("https", ssl)
            .register("http", PlainConnectionSocketFactory.INSTANCE)
            .build();
    PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
    return HttpClients.custom().setConnectionManager(cm).build();
}

七、证书轮转定时任务

@Component
public class CertRotateJob {
    @Scheduled(fixedDelay = 12 * 3600 * 1000)
    public void rotate() {
        String newVer = ACMClient.getLatestVersion();
        if (newVer.equals(CurrentVersion.get())) return;
        // 1. 下载并解密
        CertLoader.download(newVer);
        // 2. 预热SSLContext
        DynamicSslContextFactory.reload(newVer);
        // 3. 切换标识
        CurrentVersion.set(newVer);
        // 4. 老客户端优雅关闭
        HttpClientFactory.closeIdle(newVer);
    }
}

八、回调接口样例

@RestController
@RequestMapping("/meituan")
@Slf4j
public class CallbackController {
    @PostMapping("/callback")
    public String callback(HttpServletRequest req, @RequestBody String body) {
        // 验签
        boolean ok = SignUtil.verify(req, body);
        if (!ok) return "sign_error";
        // 业务处理
       霸王餐Service.handle(body);
        return "ok";
    }
}

九、灰度验证

  1. 利用 X-Client-Cert-Version header区分
  2. 老证书请求继续由原容器处理,新证书流量逐步切到 v2
  3. 观察 24h 无异常后下线老证书

十、完整配置速贴

server:
  port: 8443
  ssl:
    enabled: false   # 由Undertow接管
undertow:
  https:
    key-alias: client
    key-store-type: PKCS12
    key-store-provider: SUN

本文著作权归吃喝不愁app开发者团队,转载请注明出处!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值