2021-09-02

该博客主要介绍了如何解决AWS SES发送邮件时遇到的问题,特别是针对Gmail等邮箱的DKIM安全验证。为确保邮件安全并避免被标记为垃圾邮件,需要在DNS服务器上配置SPF, DKIM和DMARC记录。此外,提供了使用Java SES V2 SDK发送邮件的示例代码,强调了脱离沙盒环境和DNS配置的重要性。
摘要由CSDN通过智能技术生成

调用aws ses发送邮件(二)

简述

申请脱离沙盒环境后,邮件发送还有很多问题,国外邮箱包括gmail等都采用了DKIM安全验证,国内只有腾讯邮箱做了DKIM,钉钉邮箱最多只有个SPF。所以很可能导致我们发送的邮件进入别人的垃圾箱。

问题和方案

为了防止邮件在发送途中被拦截修改,一般邮件都会设置 SPF 或 DKIM 验证机制。

简单来讲,需要告诉 AWS SES 服务器我们已经取得 flitsneak.ai 域名的授权可以用 其及子域名hi@flitsneak.ai 等进行发送邮件,并且该邮件已经经过 DKIM 进行加密处理。这样 Gmail 邮箱会认为该邮件的安全性足够高才不会当成垃圾邮件或者拒收。

具体:
为了达到如上效果,需要对 flitsneak.ai 的 DNS 服务器进行相应配置,加上 DNS 的参数来取得 AWS 的信任进而不会被 Gmail 拒收,为了防备不必要的问题,需要配置 DNS 授权 ,SPF,DKIM,DMARC 这四类属性(最起码要有SPF,多多益善)。

首先需要登录到flitsneak.ai 的 DNS 服务控制平台,可能是 amazon 或者是 dreamhost 或者是其他服务商的 DNS 。 (flitsneak.ai 或者具体的子域名比如 hi.flitsneak.ai, 如果是具体的子域名下面也要相应修改 )

然后在 mella.ai 的 DNS 选项中添加这俩对应值。
添加 DNS 授权:

名称类型
_amazonses.flitsneak.aiTXTaws的ses domain认证域面板生成

添加 DKIM :

名称类型
同上生成CNAME同上生成
同上生成CNAME同上生成
同上生成CNAME同上生成

添加 SPF:

名称类型
@TXT“v=spf1 include:flitsneak.ai include:amazonses.com ~all”

DMARC 策略:

名称类型
_dmarc.mella.aiTXT“v=DMARC1;p=quarantine;pct=25;rua=mailto:dmarcreports@flitsneak.ai”

要注意DNS授权和DKIM都是aws ses服务认证domain时自动生成的,而SPF有相应的语法规范。

java aws ses v2写法

这是aws推荐的java程序发送邮件的写法,要注意前提和上一篇v1是一致的,要脱离沙盒,要有credential。
一、引入依赖
官方建议我们版本控制:

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>bom</artifactId>
                <version>2.17.29</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

然后引入我们需要的ses,当然aws还有其他优秀的util也可以引入:

<!-- https://mvnrepository.com/artifact/software.amazon.awssdk/ses -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>ses</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/javax.mail/javax.mail-api -->
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>javax.mail-api</artifactId>
            <version>1.6.2</version>
        </dependency>

直接放上改写的程序,因为需要在邮件和app之间传参,所以及改写的方法也加了参数:

import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ses.SesClient;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeBodyPart;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Properties;

import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.ses.model.SendRawEmailRequest;
import software.amazon.awssdk.services.ses.model.RawMessage;
import software.amazon.awssdk.services.ses.model.SesException;

/**
* @author flitsneak
* @ClassName
* @date 2021/8/30 17:50
*/
public class SESV2 {

   /**
    * To run this Java V2 code example, ensure that you have setup your development environment, including your credentials.
    * <p>
    * For information, see this documentation topic:
    * <p>
    * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
    */

   static final String SUBJECT = "Hi";

   static final String SENDER = "hi@flitsneak.ai";

   public static void send(
           String recipient, String UUID
   ) throws AddressException, MessagingException, IOException {

       // The email body for non-HTML email clients
       String bodyText = "Hello,\r\n" + "This is Mella. ";

       // The HTML body of the email
       String bodyHTML =
               "<html>" +
                       "<body paddingwidth='0' paddingheight='0' style='padding-top: 0; padding-bottom: 0; padding-top: 0; padding-bottom: 0; background-repeat: repeat; width: 100% !important; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; -webkit-font-smoothing: antialiased;' offset='0' toppadding='0' leftpadding='0'>" +
                       "<table width='100%' border='0' cellspacing='0' cellpadding='0' align='center' background='https://i.loli.net/2021/08/31/wUVbPe9oSdB8tTY.png' style='font-family:Helvetica, Arial,serif;'>" +
                       "</table>" +
                       "</body>"
                       + "</html>";

       Region region = Region.US_EAST_1;
       SesClient client = SesClient.builder()
               .region(region)
               .build();

       Session session = Session.getDefaultInstance(new Properties());
       MimeMessage message = new MimeMessage(session);

       // Add subject, from and to lines
       message.setSubject(SUBJECT, "UTF-8");
       message.setFrom(new InternetAddress(SENDER));
       message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipient));

       // Create a multipart/alternative child container
       MimeMultipart msgBody = new MimeMultipart("alternative");

       // Create a wrapper for the HTML and text parts
       MimeBodyPart wrap = new MimeBodyPart();

       // Define the text part
       MimeBodyPart textPart = new MimeBodyPart();
       textPart.setContent(bodyText, "text/plain; charset=UTF-8");

       // Define the HTML part
       MimeBodyPart htmlPart = new MimeBodyPart();
       htmlPart.setContent(bodyHTML, "text/html; charset=UTF-8");

       // Add the text and HTML parts to the child container
       msgBody.addBodyPart(textPart);
       msgBody.addBodyPart(htmlPart);

       // Add the child container to the wrapper object
       wrap.setContent(msgBody);

       // Create a multipart/mixed parent container
       MimeMultipart msg = new MimeMultipart("mixed");

       // Add the parent container to the message
       message.setContent(msg);

       // Add the multipart/alternative part to the message
       msg.addBodyPart(wrap);

       try {
           System.out.println("Attempting to send an email through Amazon SES " + "using the AWS SDK for Java...");

           ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
           message.writeTo(outputStream);
           ByteBuffer buf = ByteBuffer.wrap(outputStream.toByteArray());

           byte[] arr = new byte[buf.remaining()];
           buf.get(arr);

           SdkBytes data = SdkBytes.fromByteArray(arr);
           RawMessage rawMessage = RawMessage.builder()
                   .data(data)
                   .build();

           SendRawEmailRequest rawEmailRequest = SendRawEmailRequest.builder()
                   .rawMessage(rawMessage)
                   .build();

           client.sendRawEmail(rawEmailRequest);
           client.close();
       } catch (SesException e) {
           System.err.println(e.awsErrorDetails().errorMessage());
       }

   }

}

邮件也有很多安全要注意,比如背景图只能用https协议,href属性也只支持https协议,只能采用原始的表格布局,仅支持内联style,不支持class标签等等。

使用python中的pymsql完成如下:表结构与数据创建 1. 建立 `users` 表和 `orders` 表。 `users` 表有用户ID、用户名、年龄字段,(id,name,age) `orders` 表有订单ID、订单日期、订单金额,用户id字段。(id,order_date,amount,user_id) 2 两表的id作为主键,`orders` 表用户id为users的外键 3 插入数据 `users` (1, '张三', 18), (2, '李四', 20), (3, '王五', 22), (4, '赵六', 25), (5, '钱七', 28); `orders` (1, '2021-09-01', 500, 1), (2, '2021-09-02', 1000, 2), (3, '2021-09-03', 600, 3), (4, '2021-09-04', 800, 4), (5, '2021-09-05', 1500, 5), (6, '2021-09-06', 1200, 3), (7, '2021-09-07', 2000, 1), (8, '2021-09-08', 300, 2), (9, '2021-09-09', 700, 5), (10, '2021-09-10', 900, 4); 查询语句 1. 查询订单总金额 2. 查询所有用户的平均年龄,并将结果四舍五入保留两位小数。 3. 查询订单总数最多的用户的姓名和订单总数。 4. 查询所有不重复的年龄。 5. 查询订单日期在2021年9月1日至9月4日之间的订单总金额。 6. 查询年龄不大于25岁的用户的订单数量,并按照降序排序。 7. 查询订单总金额排名前3的用户的姓名和订单总金额。 8. 查询订单总金额最大的用户的姓名和订单总金额。 9. 查询订单总金额最小的用户的姓名和订单总金额。 10. 查询所有名字中含有“李”的用户,按照名字升序排序。 11. 查询所有年龄大于20岁的用户,按照年龄降序排序,并只显示前5条记录。 12. 查询每个用户的订单数量和订单总金额,并按照总金额降序排序。
06-03
这里是使用 Python 的 PyMySQL 完成上述任务的代码: ```python import pymysql # 连接数据库 conn = pymysql.connect(host='localhost', port=3306, user='root', password='123456', database='test') # 创建游标 cur = conn.cursor() # 创建 users 表 cur.execute(''' CREATE TABLE users ( id INT PRIMARY KEY, name VARCHAR(20), age INT ) ''') # 创建 orders 表 cur.execute(''' CREATE TABLE orders ( id INT PRIMARY KEY, order_date DATE, amount INT, user_id INT, FOREIGN KEY (user_id) REFERENCES users(id) ) ''') # 插入数据 users_data = [ (1, '张三', 18), (2, '李四', 20), (3, '王五', 22), (4, '赵六', 25), (5, '钱七', 28) ] orders_data = [ (1, '2021-09-01', 500, 1), (2, '2021-09-02', 1000, 2), (3, '2021-09-03', 600, 3), (4, '2021-09-04', 800, 4), (5, '2021-09-05', 1500, 5), (6, '2021-09-06', 1200, 3), (7, '2021-09-07', 2000, 1), (8, '2021-09-08', 300, 2), (9, '2021-09-09', 700, 5), (10, '2021-09-10', 900, 4) ] for user in users_data: cur.execute('INSERT INTO users VALUES (%s, %s, %s)', user) for order in orders_data: cur.execute('INSERT INTO orders VALUES (%s, %s, %s, %s)', order) # 提交事务 conn.commit() # 查询语句 # 1. 查询订单总金额 cur.execute('SELECT SUM(amount) FROM orders') print(cur.fetchone()[0]) # 2. 查询所有用户的平均年龄,并将结果四舍五入保留两位小数。 cur.execute('SELECT ROUND(AVG(age), 2) FROM users') print(cur.fetchone()[0]) # 3. 查询订单总数最多的用户的姓名和订单总数。 cur.execute(''' SELECT users.name, COUNT(*) AS total_orders FROM users JOIN orders ON users.id = orders.user_id GROUP BY users.id ORDER BY total_orders DESC LIMIT 1 ''') print(cur.fetchone()) # 4. 查询所有不重复的年龄。 cur.execute('SELECT DISTINCT age FROM users') print([row[0] for row in cur.fetchall()]) # 5. 查询订单日期在2021年9月1日至9月4日之间的订单总金额。 cur.execute('SELECT SUM(amount) FROM orders WHERE order_date BETWEEN "2021-09-01" AND "2021-09-04"') print(cur.fetchone()[0]) # 6. 查询年龄不大于25岁的用户的订单数量,并按照降序排序。 cur.execute(''' SELECT users.name, COUNT(*) AS total_orders FROM users JOIN orders ON users.id = orders.user_id WHERE age <= 25 GROUP BY users.id ORDER BY total_orders DESC ''') print(cur.fetchall()) # 7. 查询订单总金额排名前3的用户的姓名和订单总金额。 cur.execute(''' SELECT users.name, SUM(amount) AS total_amount FROM users JOIN orders ON users.id = orders.user_id GROUP BY users.id ORDER BY total_amount DESC LIMIT 3 ''') print(cur.fetchall()) # 8. 查询订单总金额最大的用户的姓名和订单总金额。 cur.execute(''' SELECT users.name, SUM(amount) AS total_amount FROM users JOIN orders ON users.id = orders.user_id GROUP BY users.id ORDER BY total_amount DESC LIMIT 1 ''') print(cur.fetchone()) # 9. 查询订单总金额最小的用户的姓名和订单总金额。 cur.execute(''' SELECT users.name, SUM(amount) AS total_amount FROM users JOIN orders ON users.id = orders.user_id GROUP BY users.id ORDER BY total_amount ASC LIMIT 1 ''') print(cur.fetchone()) # 10. 查询所有名字中含有“李”的用户,按照名字升序排序。 cur.execute('SELECT * FROM users WHERE name LIKE "%李%" ORDER BY name ASC') print(cur.fetchall()) # 11. 查询所有年龄大于20岁的用户,按照年龄降序排序,并只显示前5条记录。 cur.execute('SELECT * FROM users WHERE age > 20 ORDER BY age DESC LIMIT 5') print(cur.fetchall()) # 12. 查询每个用户的订单数量和订单总金额,并按照总金额降序排序。 cur.execute(''' SELECT users.name, COUNT(*) AS total_orders, SUM(amount) AS total_amount FROM users JOIN orders ON users.id = orders.user_id GROUP BY users.id ORDER BY total_amount DESC ''') print(cur.fetchall()) # 关闭游标和连接 cur.close() conn.close() ``` 注意:在运行代码之前,需要先安装 PyMySQL 模块,可以使用以下命令进行安装: ``` pip install pymysql ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值