基于OAuth2.0与美团开放平台规范实现外卖霸王餐API的安全认证机制
在当前互联网生态中,第三方开发者通过接入美团开放平台提供的API来扩展自身业务已成为常见模式。以"外卖霸王餐"类功能为例,其核心在于调用美团的订单、用户、门店等接口,实现免单核销、活动发放等操作。然而,这类高敏感操作必须建立在严格的安全认证机制之上。本文将围绕OAuth2.0协议与美团开放平台的认证规范,详细阐述如何安全地实现此类API调用,并提供完整的Java代码示例。
美团开放平台认证机制概述
美团开放平台采用OAuth2.0作为授权框架,结合应用密钥(appKey/appSecret)和访问令牌(access_token)进行身份验证。开发者需先在美团开放平台注册应用,获取 appKey 与 appSecret。每次调用受保护的API前,需携带有效的 access_token,该 token 通过授权码模式或客户端凭证模式获取。
对于"霸王餐"类服务,通常属于服务端到服务端(Server-to-Server)调用,推荐使用客户端凭证模式(Client Credentials Grant) ,即直接使用 appKey 和 appSecret 换取 access_token。

获取Access Token的实现
以下为使用 Java 实现获取 access_token 的核心逻辑,包路径遵循 juwatech.cn.* 命名规范:
java
package juwatech.cn.auth;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.nio.charset.StandardCharsets;
public class MeituanAuthClient {
private static final String TOKEN_URL = "https://openapi.meituan.com/oauth/access_token";
private static final String APP_KEY = "your_app_key";
private static final String APP_SECRET = "your_app_secret";
public String fetchAccessToken() throws Exception {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost post = new HttpPost(TOKEN_URL);
post.setHeader("Content-Type", "application/json");
String jsonBody = String.format(
"{\"appkey\":\"%s\",\"secret\":\"%s\",\"grant_type\":\"client_credentials\"}",
APP_KEY, APP_SECRET
);
post.setEntity(new StringEntity(jsonBody, StandardCharsets.UTF_8));
HttpResponse response = httpClient.execute(post);
String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(responseBody);
if (root.has("access_token")) {
return root.get("access_token").asText();
} else {
throw new RuntimeException("Failed to obtain access token: " + responseBody);
}
}
}
签名机制与请求构造
美团要求所有API请求必须包含签名(sign),以防止参数被篡改。签名算法为 HMAC-SHA256,使用 appSecret 对按字典序排序的请求参数进行签名。
以下为封装带签名的HTTP请求工具类:
java
package juwatech.cn.util;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
public class MeituanSignUtil {
public static String generateSign(Map<String, String> params, String appSecret) {
// 按 key 字典序排序
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
StringBuilder sb = new StringBuilder();
for (String key : keys) {
sb.append(key).append(params.get(key));
}
sb.append(appSecret);
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(secretKeySpec);
byte[] hash = mac.doFinal(sb.toString().getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder();
for (byte b : hash) {
String hexStr = Integer.toHexString(0xff & b);
if (hexStr.length() == 1) hex.append('0');
hex.append(hexStr);
}
return hex.toString().toLowerCase();
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Signature generation failed", e);
}
}
}
调用霸王餐核销API示例
假设我们要调用美团的"霸王餐核销"接口(伪接口路径 /v1/free-meal/verify),需构造带 access_token 和 sign 的请求:
java
package juwatech.cn.api;
import juwatech.cn.auth.MeituanAuthClient;
import juwatech.cn.util.MeituanSignUtil;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class FreeMealApiInvoker {
private static final String VERIFY_URL = "https://openapi.meituan.com/v1/free-meal/verify";
private static final String APP_KEY = "your_app_key";
private static final String APP_SECRET = "your_app_secret";
public String verifyFreeMeal(String orderId, String userId) throws Exception {
MeituanAuthClient authClient = new MeituanAuthClient();
String accessToken = authClient.fetchAccessToken();
Map<String, String> params = new HashMap<>();
params.put("appkey", APP_KEY);
params.put("orderid", orderId);
params.put("userid", userId);
params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
String sign = MeituanSignUtil.generateSign(params, APP_SECRET);
params.put("sign", sign);
// 构造 JSON 请求体
StringBuilder json = new StringBuilder("{");
for (Map.Entry<String, String> entry : params.entrySet()) {
json.append("\"").append(entry.getKey()).append("\":\"").append(entry.getValue()).append("\",");
}
json.setLength(json.length() - 1); // 移除最后一个逗号
json.append("}");
CloseableHttpClient client = HttpClients.createDefault();
HttpPost post = new HttpPost(VERIFY_URL);
post.setHeader("Authorization", "Bearer " + accessToken);
post.setHeader("Content-Type", "application/json");
post.setEntity(new StringEntity(json.toString(), StandardCharsets.UTF_8));
HttpResponse response = client.execute(post);
return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
}
}
安全注意事项
- appSecret 必须保密:严禁硬编码在前端或客户端代码中,应存于服务端配置中心或环境变量。
- access_token 缓存:避免频繁请求 token,可缓存至其过期前(通常2小时)。
- HTTPS 强制使用:所有与美团API的通信必须通过 HTTPS。
- 参数校验:服务端应对传入的 orderId、userId 等做合法性校验,防止注入攻击。
本文著作权归吃喝不愁app开发者团队,转载请注明出处!