如何通过Postman调用Amazon AWS SES API发送邮件?
纯API调用AWS SES发送邮件方案
作为AWS新手,直接调用SES API的核心难点是AWS Signature Version 4签名认证,这是所有AWS API的强制要求,下面给你分步说明Postman测试和Java OkHttp实现的方法,以及官方参考方向:
一、Postman调用SES SendEmail API步骤
- 准备前置条件:确保你有带SES发送权限的Access Key ID和Secret Access Key,发件邮箱已在SES中验证(sandbox模式下收件邮箱也需要验证)。
- 配置请求基础信息:
- 请求方法选
POST,端点填对应区域的SES地址,比如美东1区是https://email.us-east-1.amazonaws.com/ - 设置Headers:
Content-Type: application/x-www-form-urlencodedX-Amz-Target: SimpleEmailService_v20101201.SendEmailX-Amz-Date: UTC格式时间(示例:20240520T123456Z)
- 请求方法选
- 配置AWS签名:
- 在Postman的
Authorization标签页,类型选AWS Signature - 填入你的Access Key、Secret Key,Region选对应的区域(比如us-east-1),Service Name填
email
- 在Postman的
- 填写请求体:
- 切换到
Body标签页,选x-www-form-urlencoded,添加以下键值对:Source: 已验证的发件邮箱(示例:sender@example.com)Destination.ToAddresses.member.1: 收件邮箱(sandbox模式下需验证)Message.Subject.Data: 邮件主题Message.Body.Text.Data: 邮件正文文本内容
- 切换到
- 发送请求,返回包含
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(); } }
三、官方参考文档方向
- SES SendEmail API 文档:详细说明请求参数、区域端点、返回格式等核心信息。
- AWS Signature Version 4 规范:完整的签名逻辑说明,是实现纯API调用的核心依据。
- SES sandbox模式说明:新手默认处于sandbox模式,需验证发件和收件邮箱,或申请移出sandbox才能发送给任意邮箱。
内容的提问来源于stack exchange,提问作者Mustafa Dahodwala
相关产品推荐
相关产品推荐

