使用 AWS Pinpoint and Go 发送包含 RAW 内容的电子邮件 returns 403

Sending an email with RAW content using AWS Pinpoint and Go returns 403

我正在尝试通过 AWS pinpoint 发送包含附件的电子邮件。要使用电子邮件发送附件,您必须使用 'RAW' 电子邮件内容。我能找到的关于此的唯一文档是:https://docs.aws.amazon.com/pinpoint-email/latest/APIReference/API_RawMessage.html,但它缺少很多东西(比如,需要什么 headers?)

当我使用 'simple' 内容发送电子邮件时,它工作正常:

emailInput := &pinpointemail.SendEmailInput{
    Destination: &pinpointemail.Destination{
        ToAddresses: []*string{&address},
    },
    FromEmailAddress: &sender,
    Content: &pinpointemail.EmailContent{
                Simple: &pinpointemail.Message{
                    Body: &pinpointemail.Body{
                        Html: &pinpointemail.Content{
                            Charset: &charset,
                            Data:    &emailHTML,
                        },
                        Text: &pinpointemail.Content{
                            Charset: &charset,
                            Data:    &emailText,
                        },
                    },
                    Subject: &pinpointemail.Content{
                        Charset: &charset,
                        Data:    &emailSubject,
                    },
                },
}

因为我要添加附件,所以我必须使用 'RAW' 内容类型。我编写了一个生成电子邮件内容的函数,基于:https://gist.github.com/douglasmakey/90753ecf37ac10c25873825097f46300:

func generateRawEmailContent(subject, to, from, HTMLBody string, attachments *[]EmailAttachment) []byte {
    buf := bytes.NewBuffer(nil)
    buf.WriteString(fmt.Sprintf("Subject: %s\n", subject))
    buf.WriteString(fmt.Sprintf("To: %s\n", to))
    buf.WriteString(fmt.Sprintf("From: %s\n\n", from))

    buf.WriteString("MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n")
    buf.WriteString(HTMLBody)

    writer := multipart.NewWriter(buf)
    boundary := writer.Boundary()

    buf.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\n", boundary))
    buf.WriteString(fmt.Sprintf("--%s\n", boundary))

    for _, attachment := range *attachments {
        buf.WriteString(fmt.Sprintf("\n\n--%s\n", boundary))
        buf.WriteString(fmt.Sprintf("Content-Type: %s\n", http.DetectContentType(attachment.Data)))
        buf.WriteString("Content-Transfer-Encoding: base64\n")
        buf.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=%s\n", attachment.FileName))

        b := make([]byte, base64.StdEncoding.EncodedLen(len(attachment.Data)))
        base64.StdEncoding.Encode(b, attachment.Data)
        buf.Write(b)
        buf.WriteString(fmt.Sprintf("\n--%s", boundary))
    }

    buf.WriteString("--")

    log.Println(string(buf.Bytes()))

    return buf.Bytes()
}

这会生成以下内容(电子邮件已更改):

Subject: Welcome \nTo: xxxxx@gmail.com\nFrom: xxxxx@gmail.com\n\nMIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n\u003ch1\u003eHello ,\u003c/h1\u003e\u003cp\u003eYou now have an account.\u003c/p\u003e\nContent-Type: multipart/mixed; boundary=8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647\n--8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647\n\n\n--8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647\nContent-Type: text/plain; charset=utf-8\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment; filename=test.json\newogICJ0ZXN0IjogdHJ1ZQp9\n--8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647--

然后我按如下方式构建电子邮件:

&pinpointemail.SendEmailInput{
    Destination: &pinpointemail.Destination{
        ToAddresses: []*string{&address},
    },
    FromEmailAddress: &sender,
    Content: &pinpointemail.EmailContent{
                Raw: &pinpointemail.RawMessage{
                    Data: generateRawEmailContent(emailSubject, address, sender, emailHTML, emailAttachments),
                },
}

当通过 github.com/aws/aws-sdk-go/service/pinpoint 包裹发送此电子邮件时,我收到 403 返回,我不知道为什么。 403 意味着我试图访问的资源被禁止,但我不明白这与这里有什么关系?此外,没有关于 403 的文档,甚至是可能的响应。任何帮助将不胜感激!

我也尝试过使用库,例如​​ gomail-v2 库,如下所示:

m := gomail.NewMessage()
    m.SetHeader("From", from)
    m.SetHeader("To", to)
    m.SetHeader("Subject", subject)
    m.SetBody("text/plain", textBody)
    m.AddAlternative("text/html", HTMLBody)
    m.Attach("foo.txt", gomail.SetCopyFunc(func(w io.Writer) error {
        _, err := w.Write((*attachments)[0].Data)
        return err
    }))

    buf := bytes.NewBuffer(make([]byte, 0, 2048))
    _, werr := m.WriteTo(buf)
    if werr != nil {
        return nil, common.NewStackError(werr)
    }

但这仍然给我一个 403 错误。

我不是 Go 的人,所以这只是一种粗暴的尝试,试图绕过代码行以产生有效的 MIME 结构。

func generateRawEmailContent(subject, to, from, HTMLBody string, attachments *[]EmailAttachment) []byte {
    buf := bytes.NewBuffer(nil)
    // Creating headers by gluing together strings is precarious.
    // I'm sure there must be a better way.
    buf.WriteString(fmt.Sprintf("Subject: %s\n", subject))
    buf.WriteString(fmt.Sprintf("To: %s\n", to))
    // Remove spurious newline
    buf.WriteString(fmt.Sprintf("From: %s\n", from))

    writer := multipart.NewWriter(buf)
    boundary := writer.Boundary()

    buf.WriteString(fmt.Sprintf("MIME-Version: 1.0\n", boundary))
    buf.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\n", boundary))
    // End of headers
    buf.WriteString("\n")

    buf.WriteString(fmt.Sprintf("--%s\n", boundary))

    buf.WriteString("Content-Type: text/html; charset=\"UTF-8\";\n\n")
    buf.WriteString(HTMLBody)

    for _, attachment := range *attachments {
        buf.WriteString(fmt.Sprintf("\n\n--%s\n", boundary))
        buf.WriteString(fmt.Sprintf("Content-Type: %s\n", http.DetectContentType(attachment.Data)))
        buf.WriteString("Content-Transfer-Encoding: base64\n")
        buf.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=%s\n", attachment.FileName))

        b := make([]byte, base64.StdEncoding.EncodedLen(len(attachment.Data)))
        base64.StdEncoding.Encode(b, attachment.Data)
        buf.Write(b)
        // Don't add a second boundary here
        buf.WriteString("\n")
    }

    // Final terminating boundary, notice -- after
    buf.WriteString(fmt.Sprintf("\n--%s--\n", boundary))

    log.Println(string(buf.Bytes()))

    return buf.Bytes()
}

结果输出应该类似于

Subject: subject
To: recipient <victim@example.org>
From: me <sender@example.net>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=foobar

--foobar
Content-Type: text/html; charset="UTF-8"

<h1>Tremble, victim</h1>
<p>We don't send <tt>text/plain</tt> because we
hate our users.</p>

--foobar
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=skull_crossbones.jpg

YmluYXJ5ZGF0YQ==
--foobar--

好的,找到问题了。事实证明,这个 403 错误与我的代码无关,而是与 AWS 中的 IAM 权限有关。显然必须启用 IAM 权限才能启用 RAW 电子邮件内容。