SpringBoot 邮件服务集成配置全面解析

前言

本文以网易邮箱(及 163 邮箱)为例,展示如何为 SpringBoot 项目集成邮件服务,其他邮箱配置类似,可以自行查看 Spring Email 指南 或是其他官方文档

授权码

首先我们需要获取授权码,用于后续配置,登录邮箱: https://mail.163.com/

点击顶端设置,之后选择 POP3/SMTP/IMAP 选项

POP3/SMTP 服务已开启 – 开启该服务,开启是需要验证手机号发送验证码。

验证完成会返回授权码,该授权码只显示一次,记得保存,否则需要重新发送验证码获取新的授权码

添加依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId>
</dependency>

配置文件

Spring Boot 为 JavaMailSender 提供了自动配置以及启动器模块。

spring:mail:  default-encoding: UTF-8  host: smtp.163.com # 网站发送邮件邮箱服务 host    port: 465  username: xxx@163.com # 邮箱  password: ONSWXXXXXXXX # 授权码 protocol: smtp  properties:  mail:  smtp:  auth: 'true'  socketFactory:  class: com.rymcu.forest.util.MailSSLSocketFactory  
#            class: javax.net.ssl.SSLSocketFactory  port: 465  ssl:  enable: true  starttls:  enable: true  stattls:  required: true  connectiontimeout: 5000  timeout: 3000  writetimeout: 5000

注意:上面的 password 不是你的邮箱密码,而是我们上一章节得到的授权码

相关参数介绍

  • default-encoding: 默认编码格式,这里设置为 UTF-8。
  • host: SMTP服务器的地址,这里是163邮箱的SMTP服务器地址。
  • port: SMTP服务器的端口,163邮箱的SMTP端口是465。
  • username: 163邮箱账号。
  • password: 我们上面得到的授权码。
  • protocol: 使用的协议,这里是SMTP协议。
  • properties: 额外的属性设置。
    • mail: 邮件相关的属性。
      • smtp: SMTP相关的属性。
        • auth: 是否需要认证,这里设置为true,表示需要认证。
        • socketFactory: Socket工厂相关设置。
          • class: Socket工厂类,表示使用SSL加密。
          • port: Socket工厂使用的端口,这里也是465。
        • ssl: SSL相关设置。
          • enable: 是否启用SSL,这里设置为true,表示启用SSL加密。
        • starttls: STARTTLS相关设置。
          • enable: 是否启用STARTTLS,这里设置为true,表示启用STARTTLS。
        • stattls: STARTTLS相关设置。
          • required: 是否要求STARTTLS,这里设置为true,表示要求STARTTLS。
        • connectiontimeout: 连接超时时间,单位为毫秒,这里设置为5000毫秒(5秒)。
        • timeout: 操作超时时间,单位为毫秒,这里设置为3000毫秒(3秒)。
        • writetimeout: 写超时时间,单位为毫秒,这里设置为5000毫秒(5秒)。

更多详细信息可以查看 Spring 官方文档:36. Sending Email (spring.io)

MailProperties 源码:MailProperties.java at v2.0.3.RELEASE

如果不想要给部分配置信息写在 application.yml 中,也可以直接硬编码在代码里

        Properties props = new Properties();// 表示SMTP发送邮件,需要进行身份验证props.put("mail.smtp.auth", true);props.put("mail.smtp.ssl.enable", true);props.put("mail.smtp.host", SERVER_HOST);props.put("mail.smtp.port", SERVER_PORT);// 如果使用ssl,则去掉使用25端口的配置,进行如下配置props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");props.put("mail.smtp.socketFactory.port", SERVER_PORT);// 发件人的账号,填写控制台配置的发信地址,比如xxx@xxx.comprops.put("mail.user", USERNAME);// 访问SMTP服务时需要提供的密码(在控制台选择发信地址进行设置)props.put("mail.password", PASSWORD);// 配置mailSender.setJavaMailProperties(props);

还有就是大家可以注意到,上面指定端口为 465,这是因为 SMTP 服务默认的 25 端口阿里云默认是禁用状态,详情请看阿里云官方文档

所以如果本地调试我们不指定 port 是没问题的,但是阿里云线上是无法通过端口 25 发送邮件的,建议直接指定指定 465 端口(使用 SSL),或是 80 端口(不使用 SSL)。虽然通过一定手段可以解封 25 端口,但是比较麻烦,且成功率不高

编写代码

注:代码部分来自仓库:rymcu/forest: forest(森林),同时进行了改动

SSL 相关配置

本章节为需要使用 https 协议的相关配置,没有该需求这个小章节可以先跳过,给后面配置完后再来配置也没有影响,不过应该需要设置 spring.mail.properties.mail.smtp.ssl.enable=false 以此来关闭 ssl

MailSSLSocketFactory

package com.rymcu.forest.util;import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;public class MailSSLSocketFactory extends SSLSocketFactory {private SSLSocketFactory factory;public MailSSLSocketFactory() {try {SSLContext sslcontext = SSLContext.getInstance("TLS");sslcontext.init(null, new TrustManager[]{new MailTrustManager()}, null);factory = sslcontext.getSocketFactory();} catch (Exception ex) {// ignore}}public static SocketFactory getDefault() {return new MailSSLSocketFactory();}@Overridepublic Socket createSocket() throws IOException {return factory.createSocket();}@Overridepublic Socket createSocket(Socket socket, String s, int i, boolean flag) throws IOException {return factory.createSocket(socket, s, i, flag);}@Overridepublic Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr1, int j) throws IOException {return factory.createSocket(inaddr, i, inaddr1, j);}@Overridepublic Socket createSocket(InetAddress inaddr, int i) throws IOException {return factory.createSocket(inaddr, i);}@Overridepublic Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException {return factory.createSocket(s, i, inaddr, j);}@Overridepublic Socket createSocket(String s, int i) throws IOException {return factory.createSocket(s, i);}@Overridepublic String[] getDefaultCipherSuites() {return factory.getDefaultCipherSuites();}@Overridepublic String[] getSupportedCipherSuites() {return factory.getSupportedCipherSuites();}
}

MailTrustManager

package com.rymcu.forest.util;import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;public class MailTrustManager implements X509TrustManager {public void checkClientTrusted(X509Certificate[] cert, String authType) {// everything is trusted}public void checkServerTrusted(X509Certificate[] cert, String authType) {// everything is trusted}public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}
}

发送邮件

JavaMailService 定义邮件相关接口

public interface JavaMailService {/*** 发送验证码邮件** @param email 收件人邮箱* @return 执行结果 0:失败1:成功* @throws MessagingException*/Integer sendEmailCode(String email) throws MessagingException;/*** 发送找回密码邮件** @param email 收件人邮箱* @return 执行结果 0:失败1:成功* @throws MessagingException*/Integer sendForgetPasswordEmail(String email) throws MessagingException;/*** 发送下消息通知** @param notification* @return* @throws MessagingException*/Integer sendNotification(NotificationDTO notification) throws MessagingException;
}

JavaMailServiceImpl 邮件接口实现

@Service
public class JavaMailServiceImpl implements JavaMailService {/*** Java邮件发送器*/@Resourceprivate JavaMailSenderImpl mailSender;@Resourceprivate RedisService redisService;@Resourceprivate UserService userService;/*** thymeleaf模板引擎*/@Resourceprivate TemplateEngine templateEngine;@Value("${spring.mail.host}")private String SERVER_HOST;@Value("${spring.mail.port}")private String SERVER_PORT;@Value("${spring.mail.username}")private String USERNAME;@Value("${spring.mail.password}")private String PASSWORD;@Value("${resource.domain}")private String BASE_URL;@Overridepublic Integer sendEmailCode(String email) throws MessagingException {return sendCode(email, 0);}@Overridepublic Integer sendForgetPasswordEmail(String email) throws MessagingException {return sendCode(email, 1);}@Overridepublic Integer sendNotification(NotificationDTO notification) throws MessagingException {User user = userService.findById(String.valueOf(notification.getIdUser()));if (NotificationConstant.Comment.equals(notification.getDataType())) {String url = notification.getDataUrl();String thymeleafTemplatePath = "mail/commentNotification";Map<String, Object> thymeleafTemplateVariable = new HashMap<String, Object>(4);thymeleafTemplateVariable.put("user", notification.getAuthor().getUserNickname());thymeleafTemplateVariable.put("articleTitle", notification.getDataTitle());thymeleafTemplateVariable.put("content", notification.getDataSummary());thymeleafTemplateVariable.put("url", url);sendTemplateEmail(USERNAME,new String[]{user.getEmail()},new String[]{},"【RYMCU】 消息通知",thymeleafTemplatePath,thymeleafTemplateVariable);return 1;}return 0;}private Integer sendCode(String to, Integer type) throws MessagingException {SimpleMailMessage simpleMailMessage = new SimpleMailMessage();simpleMailMessage.setFrom(USERNAME);simpleMailMessage.setTo(to);if (type == 0) {Integer code = Utils.genCode();redisService.set(to, code, 5 * 60);simpleMailMessage.setSubject("新用户注册邮箱验证");simpleMailMessage.setText("【RYMCU】您的校验码是 " + code + ",有效时间 5 分钟,请不要泄露验证码给其他人。如非本人操作,请忽略!");mailSender.send(simpleMailMessage);return 1;} else if (type == 1) {String code = Utils.entryptPassword(to);String url = BASE_URL + "/forget-password?code=" + code;redisService.set(code, to, 15 * 60);String thymeleafTemplatePath = "mail/forgetPasswordTemplate";Map<String, Object> thymeleafTemplateVariable = new HashMap<String, Object>(1);thymeleafTemplateVariable.put("url", url);sendTemplateEmail(USERNAME,new String[]{to},new String[]{},"【RYMCU】 找回密码",thymeleafTemplatePath,thymeleafTemplateVariable);return 1;}return 0;}/*** 发送thymeleaf模板邮件** @param deliver                   发送人邮箱名 如: returntmp@163.com* @param receivers                 收件人,可多个收件人 如:11111@qq.com,2222@163.com* @param carbonCopys               抄送人,可多个抄送人 如:33333@sohu.com* @param subject                   邮件主题 如:您收到一封高大上的邮件,请查收。* @param thymeleafTemplatePath     邮件模板 如:mail\mailTemplate.html。* @param thymeleafTemplateVariable 邮件模板变量集*/public void sendTemplateEmail(String deliver, String[] receivers, String[] carbonCopys, String subject, String thymeleafTemplatePath,Map<String, Object> thymeleafTemplateVariable) throws MessagingException {String text = null;if (thymeleafTemplateVariable != null && thymeleafTemplateVariable.size() > 0) {Context context = new Context();thymeleafTemplateVariable.forEach((key, value) -> context.setVariable(key, value));text = templateEngine.process(thymeleafTemplatePath, context);}sendMimeMail(deliver, receivers, carbonCopys, subject, text, true, null);}/*** 发送的邮件(支持带附件/html类型的邮件)** @param deliver             发送人邮箱名 如: returntmp@163.com* @param receivers           收件人,可多个收件人 如:11111@qq.com,2222@163.com* @param carbonCopys         抄送人,可多个抄送人 如:3333@sohu.com* @param subject             邮件主题 如:您收到一封高大上的邮件,请查收。* @param text                邮件内容 如:测试邮件逗你玩的。 <html><body><img*                            src=\"cid:attchmentFileName\"></body></html>* @param attachmentFilePaths 附件文件路径 如:*                            需要注意的是addInline函数中资源名称attchmentFileName需要与正文中cid:attchmentFileName对应起来* @throws Exception 邮件发送过程中的异常信息*/private void sendMimeMail(String deliver, String[] receivers, String[] carbonCopys, String subject, String text,boolean isHtml, String[] attachmentFilePaths) throws MessagingException {StopWatch stopWatch = new StopWatch();stopWatch.start();MimeMessage mimeMessage = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);helper.setFrom(deliver);helper.setTo(receivers);helper.setCc(carbonCopys);helper.setSubject(subject);helper.setText(text, isHtml);// 添加邮件附件if (attachmentFilePaths != null && attachmentFilePaths.length > 0) {for (String attachmentFilePath : attachmentFilePaths) {File file = new File(attachmentFilePath);if (file.exists()) {String attachmentFile = attachmentFilePath.substring(attachmentFilePath.lastIndexOf(File.separator));long size = file.length();if (size > 1024 * 1024) {String msg = String.format("邮件单个附件大小不允许超过1MB,[%s]文件大小[%s]。", attachmentFilePath,file.length());throw new RuntimeException(msg);} else {FileSystemResource fileSystemResource = new FileSystemResource(file);helper.addInline(attachmentFile, fileSystemResource);}}}}mailSender.send(mimeMessage);stopWatch.stop();}}

JavaMailServiceTest 单元测试

/*** javaMail测试*/
class JavaMailServiceTest extends BaseServiceTest {private static final String REALITY_EMAIL = "xxxx@qq.com";@Autowiredprivate JavaMailService javaMailService;@Testvoid sendEmailCode() throws MessagingException {assertEquals(1, javaMailService.sendEmailCode(REALITY_EMAIL));}@Testvoid sendForgetPasswordEmail() throws MessagingException {assertEquals(1, javaMailService.sendForgetPasswordEmail(REALITY_EMAIL));}@Testvoid sendNotification() throws MessagingException {assertEquals(0, javaMailService.sendNotification(new NotificationDTO()));}
}

最后我们测试上面发送验证码函数:sendEmailCode (上面的 xxxx@qq.com 替换为自己的收件人邮箱 )

最终发送成功

image.png

参考链接

  • SpringBoot集成163邮件发送详细配置,从163邮箱开始配置
  • SpringBoot 之发送邮件 | SPRING TUTORIAL (dunwu.github.io)
  • SpringBoot系列(十四)集成邮件发送服务及邮件发送的几种方式
  • 阿里云服务器不能发邮件禁用25端口的三种解决方法
  • Spring Boot配置ssl发送Email_mail.smtp.ssl.enable
  • Spring Boot 发送邮件全解析

本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/287843.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【线段树二分】第十三届蓝桥杯省赛C++ A组/研究生组 Python 研究生组《扫描游戏》(C++)

【题目描述】 有一根围绕原点 O 顺时针旋转的棒 OA&#xff0c;初始时指向正上方&#xff08;Y 轴正向&#xff09;。 在平面中有若干物件&#xff0c;第 i 个物件的坐标为&#xff08;,)&#xff0c;价值为 。 当棒扫到某个物件时&#xff0c;棒的长度会瞬间增长 &#xff…

R语言ggplot2 | 热图+随机森林重要性!升级版~

&#x1f4cb;文章目录 原图复现定义ggrf_ggcor_plot()函数加载数据集一键出图函数优点 今天推出一个升级版&#xff1a; ggrf_ggcor_plot的函数。只需要输入 响应变量的矩阵和 解释变量的矩阵&#xff0c;就能轻松一键生成随机森林重要性相关性热图。 原图 所需复现的随机森…

发车,易安联签约某新能源汽车领军品牌,为科技创新保驾护航

近日&#xff0c;易安联成功签约某新能源汽车领军品牌&#xff0c;为其 数十万终端用户 建立一个全新的 安全、便捷、高效一体化的零信任终端安全办公平台。 随着新能源汽车行业的高速发展&#xff0c;战略布局的不断扩大&#xff0c;技术创新不断引领其市场价值走向高点&am…

如何在数字化转型中确保数据安全

随着科技的飞速发展&#xff0c;数字化转型已成为企业发展的必然趋势。数字化转型是指企业利用数字技术对业务流程、组织结构和商业模式进行全面创新和变革&#xff0c;以提高企业的竞争力和创新能力。然而&#xff0c;在数字化转型过程中&#xff0c;数据安全问题日益凸显&…

新能源汽车充电桩主板各模块成本占比解析

汽车充电桩主板是汽车充电桩的重要组件&#xff0c;主要由微处理器模块、通信模块、控制模块、安全保护模块、传感器模块等多个模块构成。深入探究各模块在总成本中的比重&#xff0c;我们可以更好地优化成本结构、提高生产效率&#xff0c;并为未来的技术创新和市场需求变化做…

R语言学习——Rstudio软件

R语言免费但有点难上手&#xff0c;是数据挖掘的入门级别语言&#xff0c;拥有顶级的可视化功能。 优点&#xff1a; 1统计分析&#xff08;可以实现各种分析方法&#xff09;和计算&#xff08;有很多函数&#xff09; 2强大的绘图功能 3扩展包多&#xff0c;适合领域多 …

Docker - 哲学 默认网络和 自定义网络 与 linux 网络类型 和 overlay2

默认网络&#xff1a;不指定 --nerwork 不指定 网络 run 一个容器时&#xff0c;会直接使用默认的网络桥接器 &#xff08;docker0&#xff09; 自定义网络&#xff1a;指定 --nerwork 让这两台容器互相通信 的前提 - 共享同一个网络 关于 ip addr 显示 ens160 储存驱动 ov…

入行AI写作第一个月收入2万+复盘分享

入行AI写作第一个月收入2万复盘分享 AI写作作为一种新兴的创作方式&#xff0c;正逐渐改变着内容产业的生态。在这个领域中&#xff0c;许多人通过自己的努力和智慧&#xff0c;实现了快速的成长和收入的增长。本文将从技术学习与掌握、实践与应用、内容创作与优化、持续学习与…

java 面向对象入门

类的创建 右键点击对应的包&#xff0c;点击新建选择java类 填写名称一般是名词&#xff0c;要知道大概是什么的名称&#xff0c;首字母一般大写 下面是创建了一个Goods类&#xff0c;里面的成员变量有&#xff1a;1.编号&#xff08;id&#xff09;&#xff0c;2.名称&#x…

护眼落地灯怎么选?五款好评连连的护眼大路灯曝光!

现代人越来越重视视力健康&#xff0c;而护眼落地灯则可以很好的提供良好的光线来帮助大家解决平时用眼时的不良光线困扰&#xff0c;因此&#xff0c;受到了很多人喜爱。但是&#xff0c;在产品爆火的同时&#xff0c;市场上也出现了一些质量差且劣质的护眼落地灯&#xff0c;…

Microsoft .NET 应用程序性能监控

什么是 .NET监控 Microsoft .NET 监视在确保可以开发和部署应用程序而不必面对性能滞后或中断方面发挥着重要作用。它使用警报、增长趋势报告和数据可视化技术来帮助管理员确保 Microsoft .NET 平台的全天候可用性。Microsoft.NET 性能监视是一种检测性能异常的先发制人方法&a…

linux 网卡配置 vlan/bond/bridge/macvlan/ipvlan 模式

linux 网卡模式 linux网卡支持非vlan模式、vlan模式、bond模式、bridge模式&#xff0c;macvlan模式、ipvlan模式等&#xff0c;下面介绍交换机端及服务器端配置示例。 前置要求&#xff1a; 准备一台物理交换机&#xff0c;以 H3C S5130 三层交换机为例准备一台物理服务器&…

Nacos配置中心的敏感数据加密处理

为了简化运维工作,使用nacos作为配置中心,但很多敏感数据都是明文存储的,这样一旦数据泄露,可能会造成很大影响,所以最好把这些数据进行加密处理,下面介绍几种数据的加密。 一、数据库信息加密 数据库的配置本篇介绍两种,一是使用druid连接池的,这种比较常见;二是使…

网络安全-提权篇

我们在文件包含的时候可以将错误的用户名包含进日志&#xff0c;但是权限问题让人很烦恼&#xff0c;今天的侧重点主要是跟大家聊一聊提权 用户名包含进日志参考&#xff1a;RCE with LFI and SSH Log Poisoning - Hacking Articles 目录 一、环境 二、看看权限&#xff08;…

vue指令相关

vue中有很多的指令像v-on、v-model、v-bind等是我们开发中常用的 常用指令 v-bind 单向绑定解析表达式 v-model 双向数据绑定 v-for 遍历数组/对象/字符串 v-on 绑定事件监听,可简写为@ v-show 条件渲染(动态控制节点是否存展示) v-if 条件渲染(动态控制节点是否存存在) v…

AI智聊功能支持生成旅游攻略、作文、标题优化,方便视频剪辑

在快节奏的生活中&#xff0c;我们总是需要快速、准确地获取所需信息。无论是撰写旅游攻略、作文&#xff0c;还是准备演讲稿&#xff0c;AI智聊都能为您一键生成精彩文案&#xff0c;让您的创意无限发挥&#xff01; 媒体梦工厂的AI智聊功能&#xff0c;利用先进的自然语言处…

Teamcenter 快速获取所有激活状态用户的方法

问题描述&#xff1a; License数量有限&#xff0c;需要分析Teamcenter 中所有激活状态下的用户&#xff0c;对其进行删减。如何快速获取Teamcenter 中所有激活状态用户呢&#xff1f; 解决方法&#xff1a; Teamcenter Query Builder提供了强大的自定义搜索功能。 新建查询…

软考高级架构师:云原生架构模式概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

v4l2采集视频

Video4Linux2&#xff08;v4l2&#xff09;是用于Linux系统的视频设备驱动框架&#xff0c;它允许用户空间应用程序直接与视频设备&#xff08;如摄像头、视频采集卡等&#xff09;进行交互。 linux系统下一切皆文件&#xff0c;对视频设备的操作就像对文件的操作一样&#xff…

TinyEMU源码分析之启动流程

TinyEMU源码分析之启动流程 1 始于0x10002 确定BBL入口点3 mentry.S执行过程4 启动流程小结 本文属于《 TinyEMU模拟器基础系列教程》之一&#xff0c;欢迎查看其它文章。 本文中使用的代码&#xff0c;均为伪代码&#xff0c;删除了部分源码。 1 始于0x1000 我们沿着TinyEMU…