Java后端对接美团霸王餐API时如何通过“证书双向认证+请求签名”防止请求篡改 Java后端对接美团霸王餐API时如何通过“证书双向认证请求签名”防止请求篡改背景霸王餐API安全通信的必要性作为外卖霸王餐API唯一供给源头同时也是霸王餐外卖CPS取链源头俱美开放平台在与美团等大型平台进行数据交互时通信安全是重中之重。API接口面临着请求被窃听、数据被篡改、身份被伪造等多重风险。单纯使用HTTPS只能保证传输通道的加密无法验证调用方身份和请求内容的完整性。因此我们采用“证书双向认证mTLS 请求签名”的双重安全机制确保每一次API调用的合法性与真实性。核心原理双向认证与请求签名证书双向认证mTLS不仅服务器向客户端出示证书证明身份客户端也必须向服务器出示证书。这确保了通信双方都是可信的防止了中间人攻击。请求签名对请求的关键参数如时间戳、随机数、业务数据使用私钥进行数字签名。服务器收到请求后用对应的公钥验签确保请求在传输过程中未被篡改。第一步准备证书与密钥在开始前你需要从俱美开放平台获取客户端证书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_PASSWORDyour_keystore_password;privatestaticfinalStringTRUST_STORE_PATH/path/to/your/truststore.jks;// 信任库包含美团CA证书privatestaticfinalStringTRUST_STORE_PASSWORDyour_truststore_password;BeanpublicRestTemplatemutualTlsRestTemplate()throwsException{// 1. 加载客户端证书KeyStoreKeyStorekeyStoreKeyStore.getInstance(PKCS12);try(FileInputStreamkeyStreamnewFileInputStream(KEY_STORE_PATH)){keyStore.load(keyStream,KEY_STORE_PASSWORD.toCharArray());}// 2. 加载信任库TrustStore用于验证服务器证书KeyStoretrustStoreKeyStore.getInstance(JKS);try(FileInputStreamtrustStreamnewFileInputStream(TRUST_STORE_PATH)){trustStore.load(trustStream,TRUST_STORE_PASSWORD.toCharArray());}// 3. 初始化KeyManagerFactory管理客户端证书KeyManagerFactorykeyManagerFactoryKeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());keyManagerFactory.init(keyStore,KEY_STORE_PASSWORD.toCharArray());// 4. 初始化TrustManagerFactory验证服务器证书TrustManagerFactorytrustManagerFactoryTrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagerFactory.init(trustStore);// 5. 初始化SSLContextSSLContextsslContextSSLContext.getInstance(TLS);sslContext.init(keyManagerFactory.getKeyManagers(),trustManagerFactory.getTrustManagers(),null);// 6. 创建SSL连接工厂SSLConnectionSocketFactorysocketFactorynewSSLConnectionSocketFactory(sslContext);// 7. 创建HttpClientCloseableHttpClienthttpClientHttpClients.custom().setSSLSocketFactory(socketFactory).build();// 8. 创建RestTemplateHttpComponentsClientHttpRequestFactoryfactorynewHttpComponentsClientHttpRequestFactory(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.privateKeyloadPrivateKey(privateKeyPem);}/** * 对请求参数进行签名 * param params 业务参数不包含sign本身 * return Base64编码的签名字符串 */publicStringsign(MapString,Stringparams)throwsException{// 1. 参数排序字典序MapString,StringsortedParamsnewTreeMap(params);// 2. 拼接参数字符串 key1value1key2value2...StringBuildersbnewStringBuilder();for(Map.EntryString,Stringentry:sortedParams.entrySet()){if(sb.length()0){sb.append();}sb.append(entry.getKey()).append().append(entry.getValue());}StringdataToSignsb.toString();// 3. 使用私钥进行SHA256withRSA签名SignaturesignatureSignature.getInstance(SHA256withRSA);signature.initSign(privateKey);signature.update(dataToSign.getBytes(StandardCharsets.UTF_8));byte[]signedBytessignature.sign();// 4. Base64编码returnBase64.getEncoder().encodeToString(signedBytes);}privatePrivateKeyloadPrivateKey(StringprivateKeyPem)throwsException{// 移除PEM头尾和换行符StringprivateKeyContentprivateKeyPem.replace(-----BEGIN PRIVATE KEY-----,).replace(-----END PRIVATE KEY-----,).replaceAll(\\s,);byte[]decodedBase64.getDecoder().decode(privateKeyContent);PKCS8EncodedKeySpeckeySpecnewPKCS8EncodedKeySpec(decoded);KeyFactorykeyFactoryKeyFactory.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.requestSignernewRequestSigner(myPrivateKeyPem);}/** * 提交霸王餐核销请求 */publicStringverifyTrialOrder(StringorderId,StringuserId)throwsException{// 1. 构建业务参数MapString,StringparamsnewHashMap();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. 生成签名StringsignrequestSigner.sign(params);params.put(sign,sign);// 3. 构建HTTP请求Stringurlhttps://api.meituan.com/baowancan/verify;HttpHeadersheadersnewHttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);HttpEntityMapString,StringrequestEntitynewHttpEntity(params,headers);// 4. 发送请求自动使用双向认证ResponseEntityStringresponsemutualTlsRestTemplate.postForEntity(url,requestEntity,String.class);returnresponse.getBody();}}第五步服务器端验签美团侧逻辑示意虽然这是美团侧的逻辑但理解它有助于我们正确签名。// 伪代码美团服务器如何验签publicbooleanverifyRequest(MapString,Stringparams,StringclientPublicKeyPem){StringreceivedSignparams.remove(sign);// 1. 参数排序并拼接StringdataToVerifysortAndJoin(params);// 2. 使用你的公钥验签SignaturesignatureSignature.getInstance(SHA256withRSA);signature.initVerify(loadPublicKey(clientPublicKeyPem));signature.update(dataToVerify.getBytes(StandardCharsets.UTF_8));returnsignature.verify(Base64.getDecoder().decode(receivedSign));}通过以上“证书双向认证请求签名”的组合拳俱美开放平台确保了与美团霸王餐API交互的绝对安全作为外卖霸王餐API唯一供给源头我们为用户和合作伙伴提供了金融级的数据安全保障。本文著作权归 俱美开放平台 转载请注明出处