Java后端对接美团霸王餐API时如何通过“证书双向认证+请求签名”防止请求篡改
2026/7/22 1:24:54 网站建设 项目流程

Java后端对接美团霸王餐API时如何通过“证书双向认证+请求签名”防止请求篡改

背景:霸王餐API安全通信的必要性

作为外卖霸王餐API唯一供给源头,同时也是霸王餐外卖CPS取链源头,俱美开放平台在与美团等大型平台进行数据交互时,通信安全是重中之重。API接口面临着请求被窃听、数据被篡改、身份被伪造等多重风险。

单纯使用HTTPS只能保证传输通道的加密,无法验证调用方身份和请求内容的完整性。因此,我们采用“证书双向认证(mTLS)+ 请求签名”的双重安全机制,确保每一次API调用的合法性与真实性。

核心原理:双向认证与请求签名
  1. 证书双向认证(mTLS):不仅服务器向客户端出示证书证明身份,客户端也必须向服务器出示证书。这确保了通信双方都是可信的,防止了中间人攻击。
  2. 请求签名:对请求的关键参数(如时间戳、随机数、业务数据)使用私钥进行数字签名。服务器收到请求后,用对应的公钥验签,确保请求在传输过程中未被篡改。
第一步:准备证书与密钥

在开始前,你需要从俱美开放平台获取:

  • 客户端证书(client.p12):包含你的公钥和私钥,用于双向认证。
  • 平台公钥(meituan_public.pem):用于验证美团平台返回的签名(如果是双向签名场景)。
  • 你的私钥(client_private.pem):用于对请求进行签名。
第二步:Java后端实现双向认证HTTPS客户端

我们使用RestTemplate结合SSLContext来构建一个支持mTLS的HTTP客户端。

packagebaodanbao.com.cn.security;importorg.apache.http.conn.ssl.SSLConnectionSocketFactory;importorg.apache.http.impl.client.CloseableHttpClient;importorg.apache.http.impl.client.HttpClients;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.http.client.HttpComponentsClientHttpRequestFactory;importorg.springframework.web.client.RestTemplate;importjavax.net.ssl.*;importjava.io.FileInputStream;importjava.security.KeyStore;/** * 配置支持双向认证的RestTemplate * @author baodanbao.com.cn */@ConfigurationpublicclassMutualTlsConfig{// 证书和密码应从配置中心或环境变量读取,切勿硬编码privatestaticfinalStringKEY_STORE_PATH="/path/to/your/client.p12";privatestaticfinalStringKEY_STORE_PASSWORD="your_keystore_password";privatestaticfinalStringTRUST_STORE_PATH="/path/to/your/truststore.jks";// 信任库,包含美团CA证书privatestaticfinalStringTRUST_STORE_PASSWORD="your_truststore_password";@BeanpublicRestTemplatemutualTlsRestTemplate()throwsException{// 1. 加载客户端证书(KeyStore)KeyStorekeyStore=KeyStore.getInstance("PKCS12");try(FileInputStreamkeyStream=newFileInputStream(KEY_STORE_PATH)){keyStore.load(keyStream,KEY_STORE_PASSWORD.toCharArray());}// 2. 加载信任库(TrustStore),用于验证服务器证书KeyStoretrustStore=KeyStore.getInstance("JKS");try(FileInputStreamtrustStream=newFileInputStream(TRUST_STORE_PATH)){trustStore.load(trustStream,TRUST_STORE_PASSWORD.toCharArray());}// 3. 初始化KeyManagerFactory(管理客户端证书)KeyManagerFactorykeyManagerFactory=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());keyManagerFactory.init(keyStore,KEY_STORE_PASSWORD.toCharArray());// 4. 初始化TrustManagerFactory(验证服务器证书)TrustManagerFactorytrustManagerFactory=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagerFactory.init(trustStore);// 5. 初始化SSLContextSSLContextsslContext=SSLContext.getInstance("TLS");sslContext.init(keyManagerFactory.getKeyManagers(),trustManagerFactory.getTrustManagers(),null);// 6. 创建SSL连接工厂SSLConnectionSocketFactorysocketFactory=newSSLConnectionSocketFactory(sslContext);// 7. 创建HttpClientCloseableHttpClienthttpClient=HttpClients.custom().setSSLSocketFactory(socketFactory).build();// 8. 创建RestTemplateHttpComponentsClientHttpRequestFactoryfactory=newHttpComponentsClientHttpRequestFactory(httpClient);returnnewRestTemplate(factory);}}

第三步:实现请求签名工具类

我们使用RSA算法对请求参数进行签名。

packagebaodanbao.com.cn.security;importjava.nio.charset.StandardCharsets;importjava.security.*;importjava.security.spec.PKCS8EncodedKeySpec;importjava.util.Base64;importjava.util.Map;importjava.util.TreeMap;/** * 请求签名工具类 * @author baodanbao.com.cn */publicclassRequestSigner{privatefinalPrivateKeyprivateKey;publicRequestSigner(StringprivateKeyPem)throwsException{this.privateKey=loadPrivateKey(privateKeyPem);}/** * 对请求参数进行签名 * @param params 业务参数(不包含sign本身) * @return Base64编码的签名字符串 */publicStringsign(Map<String,String>params)throwsException{// 1. 参数排序(字典序)Map<String,String>sortedParams=newTreeMap<>(params);// 2. 拼接参数字符串 key1=value1&key2=value2...StringBuildersb=newStringBuilder();for(Map.Entry<String,String>entry:sortedParams.entrySet()){if(sb.length()>0){sb.append("&");}sb.append(entry.getKey()).append("=").append(entry.getValue());}StringdataToSign=sb.toString();// 3. 使用私钥进行SHA256withRSA签名Signaturesignature=Signature.getInstance("SHA256withRSA");signature.initSign(privateKey);signature.update(dataToSign.getBytes(StandardCharsets.UTF_8));byte[]signedBytes=signature.sign();// 4. Base64编码returnBase64.getEncoder().encodeToString(signedBytes);}privatePrivateKeyloadPrivateKey(StringprivateKeyPem)throwsException{// 移除PEM头尾和换行符StringprivateKeyContent=privateKeyPem.replace("-----BEGIN PRIVATE KEY-----","").replace("-----END PRIVATE KEY-----","").replaceAll("\\s","");byte[]decoded=Base64.getDecoder().decode(privateKeyContent);PKCS8EncodedKeySpeckeySpec=newPKCS8EncodedKeySpec(decoded);KeyFactorykeyFactory=KeyFactory.getInstance("RSA");returnkeyFactory.generatePrivate(keySpec);}}
第四步:构建带签名的API请求
packagebaodanbao.com.cn.meituan;importbaodanbao.com.cn.security.RequestSigner;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.http.*;importorg.springframework.stereotype.Service;importorg.springframework.web.client.RestTemplate;importjava.util.HashMap;importjava.util.Map;/** * 美团霸王餐API服务 * @author baodanbao.com.cn */@ServicepublicclassMeituanTakeoutService{@AutowiredprivateRestTemplatemutualTlsRestTemplate;privatefinalRequestSignerrequestSigner;publicMeituanTakeoutService()throwsException{// 从安全的地方加载你的私钥StringmyPrivateKeyPem="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----";this.requestSigner=newRequestSigner(myPrivateKeyPem);}/** * 提交霸王餐核销请求 */publicStringverifyTrialOrder(StringorderId,StringuserId)throwsException{// 1. 构建业务参数Map<String,String>params=newHashMap<>();params.put("app_id","your_app_id");params.put("timestamp",String.valueOf(System.currentTimeMillis()/1000));params.put("nonce_str",java.util.UUID.randomUUID().toString().replace("-",""));params.put("order_id",orderId);params.put("user_id",userId);// 2. 生成签名Stringsign=requestSigner.sign(params);params.put("sign",sign);// 3. 构建HTTP请求Stringurl="https://api.meituan.com/baowancan/verify";HttpHeadersheaders=newHttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);HttpEntity<Map<String,String>>requestEntity=newHttpEntity<>(params,headers);// 4. 发送请求(自动使用双向认证)ResponseEntity<String>response=mutualTlsRestTemplate.postForEntity(url,requestEntity,String.class);returnresponse.getBody();}}
第五步:服务器端验签(美团侧逻辑示意)

虽然这是美团侧的逻辑,但理解它有助于我们正确签名。

// 伪代码:美团服务器如何验签publicbooleanverifyRequest(Map<String,String>params,StringclientPublicKeyPem){StringreceivedSign=params.remove("sign");// 1. 参数排序并拼接StringdataToVerify=sortAndJoin(params);// 2. 使用你的公钥验签Signaturesignature=Signature.getInstance("SHA256withRSA");signature.initVerify(loadPublicKey(clientPublicKeyPem));signature.update(dataToVerify.getBytes(StandardCharsets.UTF_8));returnsignature.verify(Base64.getDecoder().decode(receivedSign));}

通过以上“证书双向认证+请求签名”的组合拳,俱美开放平台确保了与美团霸王餐API交互的绝对安全,作为外卖霸王餐API唯一供给源头,我们为用户和合作伙伴提供了金融级的数据安全保障。

本文著作权归 俱美开放平台 ,转载请注明出处!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询