基于OAuth2.0与美团开放平台规范实现外卖霸王餐API的安全认证机制

基于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);
    }
}

安全注意事项

  1. appSecret 必须保密:严禁硬编码在前端或客户端代码中,应存于服务端配置中心或环境变量。
  2. access_token 缓存:避免频繁请求 token,可缓存至其过期前(通常2小时)。
  3. HTTPS 强制使用:所有与美团API的通信必须通过 HTTPS。
  4. 参数校验:服务端应对传入的 orderId、userId 等做合法性校验,防止注入攻击。

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

相关推荐
车载testing1 天前
SOME/IP 协议中发送 RR 报文的实践指南
网络·tcp/ip·安全
Coder个人博客1 天前
Linux6.19-ARM64 mm ioremap子模块深入分析
linux·安全·车载系统·系统架构·系统安全·鸿蒙系统·安全架构
码农三叔1 天前
(9-1)电源管理与能源系统:电池选择与安全
人工智能·嵌入式硬件·安全·机器人·能源·人形机器人
阿里云云原生1 天前
探秘 AgentRun丨动态下发+权限隔离,重构 AI Agent 安全体系
人工智能·安全·阿里云·重构·agentrun
智驱力人工智能1 天前
货车走快车道检测 高速公路安全治理的工程实践与价值闭环 高速公路货车占用小客车道抓拍系统 城市快速路货车违规占道AI识别
人工智能·opencv·算法·安全·yolo·目标检测·边缘计算
上海云盾-小余1 天前
DDoS安全防护怎么选,如何挑选DDoS防护
安全·ddos
米羊1211 天前
威胁识别(上)
网络·安全·web安全
白帽子黑客罗哥1 天前
护网行动中遇到突发安全事件的标准应急响应流程
网络·安全·web安全·计算机·护网行动
世界尽头与你1 天前
(修复方案)CVE-2023-26111: node-static 路径遍历漏洞
安全·网络安全·渗透测试
天途小编1 天前
北京昌平无人机适飞空域正式启用!附官方查询通道与安全飞行指南
安全·无人机