boot项目配置邮箱发送

最近项目准备进入测试阶段,时间相对充沛些,便对邮箱的信息发送记录下!

邮箱设置-开启smtp协议及获取授权码

以QQ邮箱为例,其他邮箱大同小异!

开启协议

 获取授权码

具体代码

基于javax.mail实现

原文可看 前辈帖子

pom.xml
<!-- 邮件 -->
<dependency>
	<groupId>com.sun.mail</groupId>
	<artifactId>javax.mail</artifactId>
	<version >1.5.4 </version >
</dependency>
EmailVO.java类(前端上送参数)
package com.example.demo.vo;

import lombok.Data;

import java.util.List;

/**
 * @Auther: lr
 * @Date: 2024/6/12 16:14
 * @Description:
 */
@Data
public class EmailVO {

    private String content; //正文
    private String subject; //主题
    private List<String> toEmailPerson; //收件人
    private List<String> ccMail;  //抄送人
    private List<String> bccMail; //密送人
    private List<String> fileNames; //附件
}
EmailPropertiesConstant常量信息(一般会在系统设置中配置)
package com.example.demo.constant;

import lombok.Data;

/**
 * @Auther: lr
 * @Date: 2024/6/12 17:11
 * @Description:
 */
@Data
public class EmailPropertiesConstant {

    // 发件人smtp邮箱服务地址
    private String emailSmtpHost = "smtp.qq.com";
    // 发件人smtp端口
    private String emailSmtpPort = "25";
    // 开启TLS加密
    private String emailSmtpIsneedssl = "1";
    // 开启验证
    private String emailSmtpIsneedauth = "1";
    // 发件人邮箱地址
    private String userName = "xxx@qq.com";
    // smtp邮箱授权码
    private String userPassword = "xxx";
    // 发件人昵称
    private String userNicky = "测试人";

    // 邮箱开关状态 0关闭 1开启
    private String status = "1";
}
EmailUtil类(发送邮件)
package com.example.demo.util;

import com.example.demo.constant.EmailPropertiesConstant;
import com.example.demo.vo.EmailVO;
import com.sun.mail.util.MailSSLSocketFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.util.CollectionUtils;

import javax.activation.FileDataSource;
import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Date;
import java.util.HashMap;
import java.util.Properties;

/**
 * @Auther: lr
 * @Date: 2024/6/13 10:38
 * @Description:
 */
public class EmailUtil {

    private static final Logger logger = LoggerFactory.getLogger(EmailUtil.class);

    /**
     * 发送邮件
     * @param emailPropertiesConstant 系统信息配置
     * @param emailVO 邮件信息
     * @return
     */
    public static HashMap<String, Object> sendEmail(EmailPropertiesConstant emailPropertiesConstant,
                                                    EmailVO emailVO) {

        HashMap<String, Object> resultMap = new HashMap<>();

        if ("0".equals(emailPropertiesConstant.getStatus())) {
            logger.error("邮箱未开启");
            resultMap.put("code", 500);
            resultMap.put("message", "邮箱未开启!");
            return resultMap;
        }

        if (CollectionUtils.isEmpty(emailVO.getToEmailPerson())) {
            logger.error("邮件收件人为空");
            resultMap.put("code", 500);
            resultMap.put("message", "邮件收件人为空!");
            return resultMap;
        }

        try {
            // 设置参数
            Properties properties = System.getProperties();
            // smtp服务地址
            properties.put("mail.smtp.host", emailPropertiesConstant.getEmailSmtpHost());
            // smtp服务端口
            properties.put("mail.smtp.port", emailPropertiesConstant.getEmailSmtpPort());
            // 开启验证
            properties.put("mail.smtp.auth", "1".equals(emailPropertiesConstant.getEmailSmtpIsneedauth()) ? "true" : "false");
            // 开启TLS加密
            properties.put("mail.smtp.starttls.enable", "1".equals(emailPropertiesConstant.getEmailSmtpIsneedssl()) ? "true" : "false");
            // 是否启用socketFactory,默认为true
            properties.put("mail.smtp.socketFactory.fallback", "true");

            MailSSLSocketFactory sf = new MailSSLSocketFactory();
            sf.setTrustAllHosts(true);
            properties.put("mail.smtp.ssl.enable", "true");
            properties.put("mail.smtp.ssl.socketFactory", sf);
            // 建立会话,利用内部类将邮箱授权给jvm
            Session session = Session.getDefaultInstance(properties, new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(emailPropertiesConstant.getUserName(), emailPropertiesConstant.getUserPassword());
                }
            });
            // 设置为true可以在控制台打印发送过程,生产环境关闭
            session.setDebug(true);
            // 创建邮件对象
            MimeMessage message = new MimeMessage(session);
            // 通过MimeMessageHelper设置正文和附件,否则会导致两者显示不全
            MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");

            //设置发件人 to为收件人,cc为抄送,bcc为密送
            helper.setFrom(new InternetAddress(emailPropertiesConstant.getUserName(), emailPropertiesConstant.getUserNicky()));
            helper.setTo(InternetAddress.parse(String.join(",", emailVO.getToEmailPerson()), false));
            if (!CollectionUtils.isEmpty(emailVO.getCcMail())) {
                helper.setCc(InternetAddress.parse(String.join(",", emailVO.getCcMail()), false));
            }
            if (!CollectionUtils.isEmpty(emailVO.getBccMail())) {
                helper.setBcc(InternetAddress.parse(String.join(",", emailVO.getBccMail()), false));
            }
            // 设置邮件主题
            helper.setSubject(emailVO.getSubject());
            //设置邮件正文内容
            helper.setText(emailVO.getContent());
            //设置发送的日期
            helper.setSentDate(new Date());
            // 设置附件(注意这里的fileName必须是服务器本地文件名,不能是远程文件链接)
            if (!CollectionUtils.isEmpty(emailVO.getFileNames())) {
                for (String fileName : emailVO.getFileNames()) {
                    FileDataSource fileDataSource = new FileDataSource(fileName);
                    helper.addAttachment(fileDataSource.getName(), fileDataSource);
                }
            }
            //调用Transport的send方法去发送邮件
            Transport.send(message);
            logger.info("发送邮件成功****************************!");
            resultMap.put("code", 200);
            resultMap.put("message", "邮件发送成功!");
            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("邮件发送失败****************************!", e);
            resultMap.put("code", 500);
            resultMap.put("message", "邮件发送失败");
            resultMap.put("data", e);
            return resultMap;
        }
    }
}
EmailController类
package com.example.demo.controller;

import com.example.demo.constant.EmailPropertiesConstant;
import com.example.demo.util.EmailUtil;
import com.example.demo.vo.EmailVO;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

/**
 * @Auther: lr
 * @Date: 2024/6/12 10:30
 * @Description:
 */
@RestController
@RequestMapping("/email")
public class EmailController {

    @PostMapping("/testEmail")
    public HashMap<String, Object> testEmail(@RequestBody EmailVO emailVO) {

        HashMap<String, Object> hashMap = EmailUtil.sendEmail(new EmailPropertiesConstant(), emailVO);
        return hashMap;
    }
}
测试
注意点

mail.smtp.auth,默认是true。一般用来测试QQ邮箱需要开启认证,而当时项目需求是客户方用内网连接邮箱服务器,此时不需要账号密码验证,所以设置为mail.smtp.auth=fale即可。但是当时生产上同时设置了mail.smtp.starttls.enable=false,未出现使用异常,到目前还是比较疑惑这个参数的设置。

发送邮件时 需要有网!!! 局域网也可。

org.springframework.mail.MailSendException: Mail server connection failed; nested exception is javax.mail.MessagingException: Exception reading response;
  nested exception is:
    javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?. Failed messages: javax.mail.MessagingException: Exception reading response;
  nested exception is:
    javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?

解决办法:mail.smtp.socketFactory.fallback=true

com.sun.mail.smtp.SMTPSendFailedException: 503 Error: need EHLO and AUTH first !

解决办法:mail.smtp.auth=true

基于spring-boot-starter-mail实现

系统参数是配置到application.yml文件中的,不利于系统维护邮件配置信息!!!

pom.xml
<!-- 邮件 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
application.yml文件
spring:
  mail:
    host: smtp.qq.com    #发件人smtp邮箱服务地址
    username: xxxx@qq.com    #发件人邮箱地址
    password: xxx    #smtp邮箱授权码
    default-encoding: UTF-8
    properties:
      userNicky: test    #发件人昵称
      status: 1    #邮箱开关状态 0关闭 1开启
      mail:
        smtp:
          auth: true    #开启加密
          ssl:
            enable: true
          starttls:
            enable: true    #开启TLS验证
          socketFactory:
            fallback: true
        debug: true
    port: 25    #发件人smtp端口
EmailUtil文件
package com.example.demo.util;

import com.example.demo.vo.EmailVO;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.activation.FileDataSource;
import javax.mail.internet.MimeMessage;
import java.util.HashMap;

/**
 * @Auther: lr
 * @Date: 2024/6/14 10:14
 * @Description:
 */

@Component
@AllArgsConstructor
public class EmailUtil {

    private static final Logger logger = LoggerFactory.getLogger(EmailUtil.class);

    @Autowired
    JavaMailSender javaMailSender;

    @Autowired
    MailProperties mailProperties;

    /**
     * 发送邮件
     * @param emailVO 邮件信息
     * @return
     */
    public HashMap<String, Object> sendEmail(EmailVO emailVO){

        HashMap<String, Object> resultMap = new HashMap<>();

        if ("0".equals(mailProperties.getProperties().get("status"))) {
            logger.error("邮箱未开启");
            resultMap.put("code", 500);
            resultMap.put("message", "邮箱未开启!");
            return resultMap;
        }

        if (CollectionUtils.isEmpty(emailVO.getToEmailPerson())) {
            logger.error("邮件收件人为空");
            resultMap.put("code", 500);
            resultMap.put("message", "邮件收件人为空!");
            return resultMap;
        }

        try {
            MimeMessage message = javaMailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(mailProperties.getUsername(), mailProperties.getProperties().get("userNicky"));
            helper.setTo(String.join(",", emailVO.getToEmailPerson()));
            if (!CollectionUtils.isEmpty(emailVO.getCcMail())) {
                helper.setCc(String.join(",", emailVO.getCcMail()));
            }
            if (!CollectionUtils.isEmpty(emailVO.getBccMail())) {
                helper.setBcc(String.join(",", emailVO.getBccMail()));
            }
            helper.setSubject(emailVO.getSubject());
            helper.setText(emailVO.getContent());
            // 设置附件(注意这里的fileName必须是服务器本地文件名,不能是远程文件链接)
            if (!CollectionUtils.isEmpty(emailVO.getFileNames())) {
                for (String fileName : emailVO.getFileNames()) {
                    FileDataSource fileDataSource = new FileDataSource(fileName);
                    helper.addAttachment(fileDataSource.getName(), fileDataSource);
                }
            }
            javaMailSender.send(message);
            logger.info("发送邮件成功****************************!");
            resultMap.put("code", 200);
            resultMap.put("message", "邮件发送成功!");
            return resultMap;
        }catch (Exception e) {
            e.printStackTrace();
            logger.error("邮件发送失败****************************!", e);
            resultMap.put("code", 500);
            resultMap.put("message", "邮件发送失败");
            resultMap.put("data", e);
            return resultMap;
        }
    }
}
controller
package com.example.demo.controller;

import com.example.demo.constant.EmailPropertiesConstant;
import com.example.demo.util.EmailUtil;
import com.example.demo.vo.EmailVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

/**
 * @Auther: lr
 * @Date: 2024/6/12 10:30
 * @Description:
 */
@RestController
@RequestMapping("/email")
public class EmailController {

    @Autowired
    EmailUtil emailUtil;

    @PostMapping("/test")
    public HashMap<String, Object> test(@RequestBody EmailVO emailVO) {

        HashMap<String, Object> hashMap = emailUtil.sendEmail(emailVO);
        return hashMap;
    }
}
测试

目前接触到的项目使用过这两种方式,简单记录下!

伪代码-简单记录逻辑(代码不通,介意勿看!)

为了方便查看邮件的进度情况,一般会新建一张消息通知表记录。

遍历要发送邮件的用户,对常见情况进行判断
1.系统邮箱开关未开启
2.邮件发送过于频繁
3.邮箱格式不正确
4.用户邮箱已存在出现重复
5.个人邮箱开关未开启
6.不在邮箱白名单内
7.用户邮箱为空

消息情况的处理逻辑

/**
 * @param mailSubject 主题
 * @param content      内容
 * @param mailAffixDir 附件地址
 * @param userIdList  发送用户集合list
 * @return
 */
public Map<String, Object> sendSMSMessage(String mailSubject,String content,List<String> mailAffixDir,
										  String attachment,List<Integer> userIdList) {

	//1.获取系统邮箱配置信息
	EmailPropertiesConstant propertiesConstant=new EmailPropertiesConstant();
	//根据用户ID集合查询用户信息
	List<UserInfo> userInfoList=new ArrayList<>();
	//用户邮箱集合
	Set<String> setEmail = new HashSet<>();
	//消息集合
	List<Notice> noticeList = new ArrayList<>();

	if("1".equals(propertiesConstant.getStatus())){
		//2.遍历用户对常见情况进行判断
		for(UserInfo userInfo:userInfoList) {
			//消息对象
			Notice notice = new Notice();
			notice.setuserid(userInfo.getid());
			notice.setsendstatus("1"); //1 进行中 2成功 3失败
			//若用户的邮箱非空及个人邮箱开关开着及在白名单内
			if (userInfo.getEmail() != null && "1".equals(userInfo.getEmailStatus()) && "1".equals(userInfo.getRecvEmail())) {
				if (setEmail.contains(userInfo.getEmail())) {
					//用户邮箱已存在出现重复 TODO
					notice.seterrorInfo("用户邮箱已存在出现重复!");
					notice.setsendstatus("3");
					noticeMapper.addNotice(notice);
					continue;
				}
				//判断用户的发送频率
				boolean ifSend = this.isSendTime(content,userInfo.getId(), taskNoticeType);
				if (ifSend) {
					// 正则验证邮箱
					if (RegularUtil.emailRegular(userUtil.getEmail()) == true) {
						setEmail.add(userUtil.getEmail());
						noticeList.add(notice);
					} else {
						//邮箱格式不正确! TODO
						notice.seterrorInfo("邮箱格式不正确!");
						notice.setsendstatus("3");
					}
				} else {
					//邮件发送过于频繁 TODO
					notice.seterrorInfo("邮件发送过于频繁!");
					notice.setsendstatus("3");
				}
			}else if (!"1".equals(userUtil.getIsOpenEmail())) {
				//个人邮箱开关未开启! TODO
				notice.seterrorInfo("个人邮箱开关未开启!");
				notice.setsendstatus("3");
			}else if (!"1".equals(userInfo.getRecvEmail())){
				//不在邮箱白名单内!TODO
				notice.seterrorInfo("不在邮箱白名单内!");
				notice.setsendstatus("3");
			}else if (StringUtil.isEmpty(userInfo.getEmail())) {
				//用户邮箱为空! TODO
				notice.seterrorInfo("用户邮箱为空!");
				notice.setsendstatus("3");
			}
			//消息通知入库 TODO
			noticeMapper.add(notice);
		}
	}else{
		//系统邮箱未开启 TODO
	}
	//3.整理发送邮件内容 TODO
	propertiesConstant.setMailSubject(mailSubject);
	propertiesConstant.setMailContent(content);
	propertiesConstant.setMailAffixDir(attachment);
	String emailPersons=String.join(",",setEmail);
	propertiesConstant.setMailTo(emailPersons);
	if(propertiesConstant!=null&&propertiesConstant.getMailTo()!=null&&!"".equals(propertiesConstant.getMailTo())) {
		//4.异步发送邮件 TODO
		emailWriteTxt(propertiesConstant, noticeList,3);
	}
}

异步发送邮件(异步方法不可与主方法在同一个类中)及重试

详情可见 @Async注解的坑

@Override
@Async("asyncServiceEmail")
public void emailWriteTxt(EmailPropertiesConstant propertiesConstant, List<Notice> noticeList,int retryTimes) {
	logger.info("线程(Email)-" + Thread.currentThread().getId() + "在执行写入");
	if(propertiesConstant!=null){
		//发送邮件 TODO
		HashMap<String,Object> resultMap=sendEmailUtil.sendEmail();
		//邮件发送成功
		if("200".equals(resultMap.get("code"))){
			logger.info("邮箱:" + propertiesConstant.getMailTo() + "发送成功!");
			// 遍历消息集合 对状态为非失败的消息 根据消息id 修改状态为成功 TODO
		}else{
			//邮件发送失败后重试 TODO
			retryTimes(propertiesConstant,noticeList,retryTimes);
		}
	}
}

//重试
public boolean retryTimes(EmailPropertiesConstant propertiesConstant, List<Notice> noticeList,int retryTimes) {
	String result = "";
	int i = 1;
	HashMap<String,Object> resultMap = new HashMap<>();
	// 如果失败 则进行重试
	while (i <= retryTimes) {
		//发送邮件 TODO
		resultMap=sendEmailUtil.sendEmail();
		//邮件发送成功
		if("200".equals(resultMap.get("code"))){
			// 遍历消息集合 对状态为非失败的消息 根据消息id 修改状态为成功 TODO
			return true;
		}
		result =resultMap.get("data").toString();
		logger.error("邮箱号:" + propertiesConstant.getMailTo() + "第" + i + "次重试……失败……"+result);
		i++;
	}
	// 遍历消息集合 对状态为非失败的消息 根据消息id 修改状态为失败 TODO
	for (Notice notice : noticeList) {
		if(!notice.getsendstatus().equals("2")){
			noticeMapper.update(notice.getId(),"3","邮箱服务器异常!"+result,3);
		}
	}
	return false;
}


 

  • 20
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot可以使用JavaMailSender来发送邮件。下面是使用QQ邮箱发送邮件的步骤: 1. 在QQ邮箱中开启SMTP服务,并生成授权码。 2. 在pom.xml中添加依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> ``` 3. 在application.properties中配置邮箱信息: ```properties spring.mail.host=smtp.qq.com spring.mail.port=587 spring.mail.username=你的QQ邮箱 spring.mail.password=你的授权码 spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true ``` 4. 创建一个邮件服务类: ```java @Service public class MailService { @Autowired private JavaMailSender mailSender; public void sendMail(String to, String subject, String content) { SimpleMailMessage message = new SimpleMailMessage(); message.setFrom("你的QQ邮箱"); message.setTo(to); message.setSubject(subject); message.setText(content); mailSender.send(message); } } ``` 5. 在需要发送邮件的地方调用MailService的sendMail方法即可。 ```java @Autowired private MailService mailService; mailService.sendMail("收件人邮箱", "邮件主题", "邮件内容"); ``` 注意事项: 1. 需要在QQ邮箱中开启SMTP服务,并生成授权码。 2. 如果使用SSL,需要将port改为465,并配置spring.mail.properties.mail.smtp.ssl.enable=true。 3. 邮箱的用户名和密码需要进行加密处理,可以使用spring-boot-starter-security依赖来加密。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值