You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Postman调用Amazon AWS SES API发送邮件?

纯API调用AWS SES发送邮件方案

作为AWS新手,直接调用SES API的核心难点是AWS Signature Version 4签名认证,这是所有AWS API的强制要求,下面给你分步说明Postman测试和Java OkHttp实现的方法,以及官方参考方向:

一、Postman调用SES SendEmail API步骤

  1. 准备前置条件:确保你有带SES发送权限的Access Key ID和Secret Access Key,发件邮箱已在SES中验证(sandbox模式下收件邮箱也需要验证)。
  2. 配置请求基础信息:
    • 请求方法选POST,端点填对应区域的SES地址,比如美东1区是https://email.us-east-1.amazonaws.com/
    • 设置Headers:
      • Content-Type: application/x-www-form-urlencoded
      • X-Amz-Target: SimpleEmailService_v20101201.SendEmail
      • X-Amz-Date: UTC格式时间(示例:20240520T123456Z)
  3. 配置AWS签名:
    • 在Postman的Authorization标签页,类型选AWS Signature
    • 填入你的Access Key、Secret Key,Region选对应的区域(比如us-east-1),Service Name填email
  4. 填写请求体:
    • 切换到Body标签页,选x-www-form-urlencoded,添加以下键值对:
      • Source: 已验证的发件邮箱(示例:sender@example.com)
      • Destination.ToAddresses.member.1: 收件邮箱(sandbox模式下需验证)
      • Message.Subject.Data: 邮件主题
      • Message.Body.Text.Data: 邮件正文文本内容
  5. 发送请求,返回包含MessageId的JSON即为成功。

二、Java OkHttp纯API实现

核心是自己实现AWS SigV4签名逻辑,下面是可运行的简化示例(关键签名逻辑已完整实现):

import okhttp3.*;
import java.io.IOException;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class SesDirectApiCall {
    private static final String AWS_REGION = "us-east-1";
    private static final String SES_ENDPOINT = "https://email." + AWS_REGION + ".amazonaws.com/";
    private static final String AWS_ACCESS_KEY = "你的AWS Access Key";
    private static final String AWS_SECRET_KEY = "你的AWS Secret Key";
    private static final String SERVICE_NAME = "email";
    private static final String SIGNING_ALGORITHM = "AWS4-HMAC-SHA256";

    public static void main(String[] args) throws IOException {
        OkHttpClient client = new OkHttpClient();

        // 构建请求体
        FormBody formBody = new FormBody.Builder()
                .add("Source", "verified-sender@example.com")
                .add("Destination.ToAddresses.member.1", "recipient@example.com")
                .add("Message.Subject.Data", "测试邮件(纯API调用)")
                .add("Message.Body.Text.Data", "这是通过直接调用SES API发送的测试邮件")
                .build();

        // 获取UTC时间戳,用于签名
        String amzDate = ZonedDateTime.now(java.time.ZoneOffset.UTC)
                .format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
        String dateStamp = amzDate.substring(0, 8);

        // 生成Authorization头
        String authorizationHeader = buildAuthorizationHeader(formBody, amzDate, dateStamp);

        Request request = new Request.Builder()
                .url(SES_ENDPOINT)
                .post(formBody)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .addHeader("X-Amz-Target", "SimpleEmailService_v20101201.SendEmail")
                .addHeader("X-Amz-Date", amzDate)
                .addHeader("Authorization", authorizationHeader)
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) throw new IOException("请求失败:" + response.code());
            System.out.println("发送成功,响应:" + response.body().string());
        }
    }

    private static String buildAuthorizationHeader(FormBody formBody, String amzDate, String dateStamp) {
        try {
            // 1. 生成签名密钥
            byte[] signingKey = getSigningKey(AWS_SECRET_KEY, dateStamp, AWS_REGION, SERVICE_NAME);

            // 2. 构建规范请求
            String canonicalRequest = buildCanonicalRequest(formBody, amzDate);

            // 3. 构建待签名字符串
            String stringToSign = buildStringToSign(amzDate, dateStamp, canonicalRequest);

            // 4. 计算签名
            String signature = calculateSignature(signingKey, stringToSign);

            // 5. 构建Authorization头
            return String.format("%s Credential=%s/%s/%s/%s/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-target, Signature=%s",
                    SIGNING_ALGORITHM, AWS_ACCESS_KEY, dateStamp, AWS_REGION, SERVICE_NAME, signature);

        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            throw new RuntimeException("签名生成失败", e);
        }
    }

    // 生成AWS SigV4签名密钥
    private static byte[] getSigningKey(String secretKey, String dateStamp, String regionName, String serviceName) throws NoSuchAlgorithmException, InvalidKeyException {
        byte[] kSecret = ("AWS4" + secretKey).getBytes();
        byte[] kDate = hmacSha256(kSecret, dateStamp.getBytes());
        byte[] kRegion = hmacSha256(kDate, regionName.getBytes());
        byte[] kService = hmacSha256(kRegion, serviceName.getBytes());
        return hmacSha256(kService, "aws4_request".getBytes());
    }

    // 构建规范请求
    private static String buildCanonicalRequest(FormBody formBody, String amzDate) {
        String canonicalUri = "/";
        String canonicalQueryString = "";
        // 规范头需按字母排序
        String canonicalHeaders = String.format("content-type:%s\nhost:%s\nx-amz-date:%s\nx-amz-target:%s\n",
                "application/x-www-form-urlencoded", "email." + AWS_REGION + ".amazonaws.com", amzDate, "SimpleEmailService_v20101201.SendEmail");
        String signedHeaders = "content-type;host;x-amz-date;x-amz-target";
        // 计算请求体的SHA256哈希
        String payloadHash = hashSha256(formBodyToString(formBody));

        return String.format("POST\n%s\n%s\n%s\n%s\n%s",
                canonicalUri, canonicalQueryString, canonicalHeaders, signedHeaders, payloadHash);
    }

    // 将FormBody转为字符串用于计算哈希
    private static String formBodyToString(FormBody formBody) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < formBody.size(); i++) {
            if (i > 0) sb.append("&");
            sb.append(formBody.name(i)).append("=").append(formBody.value(i));
        }
        return sb.toString();
    }

    // 构建待签名字符串
    private static String buildStringToSign(String amzDate, String dateStamp, String canonicalRequest) {
        String credentialScope = String.format("%s/%s/%s/aws4_request", dateStamp, AWS_REGION, SERVICE_NAME);
        return String.format("%s\n%s\n%s\n%s",
                SIGNING_ALGORITHM, amzDate, credentialScope, hashSha256(canonicalRequest));
    }

    // 计算HMAC-SHA256
    private static byte[] hmacSha256(byte[] key, byte[] data) throws NoSuchAlgorithmException, InvalidKeyException {
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(key, "HmacSHA256"));
        return mac.doFinal(data);
    }

    // 计算SHA256哈希并转为十六进制
    private static String hashSha256(String data) {
        try {
            java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
            byte[] hash = md.digest(data.getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : hash) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("哈希计算失败", e);
        }
    }

    // 计算签名并转为十六进制
    private static String calculateSignature(byte[] signingKey, String stringToSign) throws NoSuchAlgorithmException, InvalidKeyException {
        byte[] signatureBytes = hmacSha256(signingKey, stringToSign.getBytes());
        StringBuilder sb = new StringBuilder();
        for (byte b : signatureBytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
}

三、官方参考文档方向

  1. SES SendEmail API 文档:详细说明请求参数、区域端点、返回格式等核心信息。
  2. AWS Signature Version 4 规范:完整的签名逻辑说明,是实现纯API调用的核心依据。
  3. SES sandbox模式说明:新手默认处于sandbox模式,需验证发件和收件邮箱,或申请移出sandbox才能发送给任意邮箱。

内容的提问来源于stack exchange,提问作者Mustafa Dahodwala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:53:13