<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-mailartifactId>
dependency>
# 邮件服务器域名
spring.mail.host=smtp.163.com
# 邮件服务器端口号(默认就是25)
spring.mail.port=25
# 发送邮件的邮箱
spring.mail.username=666666@163.com
# 该邮箱对应的授权码
spring.mail.password=123456
# 超时时间
spring.mail.timeout=20000
## 编码
spring.mail.encoding=UTF-8
# 是否校验
spring.mail.auth=true
# 协议
spring.mail.protocol=smtp
# ssl 传输使用ssl协议
# spring.mail.smtp.ssl.enable=false
spring-boot-starter-mail 会根据application配置文件中配置的属性,创建 JavaMailSender实例(JavaMailSenderImpl)
建议测试的时候,使用硬编码方式,这样可以随时修改参数。
@Controller
public class SenderMailController {
@Autowired
private JavaMailSender sender;//通过application配置文件的属性创建的JavaMailSender 实例。创建实例的实现类是JavaMailSenderImpl
@RequestMapping("/testMail")
public String testMail() throws MessagingException, UnsupportedEncodingException {
//手动创建的实例的属性和配置文件中的大致相同
JavaMailSenderImpl sender=new JavaMailSenderImpl();
sender.setDefaultEncoding("utf8"); //编码
sender.setHost("smtp.163.com");//163 smtp服务器
sender.setPort(25); //端口
sender.setUsername("666666@163.com"); //邮箱
sender.setPassword("123456");//授权码
sender.setProtocol("smtp"); //协议
//配置额外属性
Properties properties=new Properties();//额外设置的属性
properties.setProperty("mail.smtp.auth", "true");//是否需要验证
properties.setProperty("mail.smtp.timeout","2000");//超时
// properties.setProperty("mail.smtp.ssl.enable", "true");//ssl加密
sender.setJavaMailProperties(properties);
//编辑信息
MimeMessage mimeMessage =sender.createMimeMessage();//多媒体信息
MimeMessageHelper helper=new MimeMessageHelper(mimeMessage,true);// 使用MimeMessageHelper类可以简化代码,提高开发效率
helper.setFrom("666666@163.com", "xxx");//设置发送邮件和发送人,该邮箱需要和上面的userName相同
helper.setTo(new String[] {"xxxxx@qq.com"});//目标地址
helper.setSubject("测试!!!");//设置邮件主题
helper.setText("测试呀
",true);//正文
File file =new File("D:/", "test.pdf");
helper.addAttachment(MimeUtility.encodeWord("测试.pdf"), file);//添加附件,并使用MimeUtility解决附件名称中文乱码
sender.send(mimeMessage);//发送邮件
}
}
简化版本
@Component
public class SendMailUtils {
@Autowired
private JavaMailSender sender;//有spring容器创建
@Value("${spring.mail.username}")
private String username;//使用值注入,避免邮箱发送地址设置错误
/**
*
* @param to 接受人,邮箱数组字符串
* @param subject 标题
* @param content 文本
* @throws MessagingException
* @throws UnsupportedEncodingException
*/
public void simpleSendMail(String[] to,String subject,String content) throws MessagingException, UnsupportedEncodingException {
MimeMessage simpleMessage =sender.createMimeMessage();
MimeMessageHelper helper=new MimeMessageHelper(simpleMessage,true);
helper.setFrom(username, "邮箱发送人说明");
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content,true);//html 数据
sender.send(simpleMessage);
}
mail.smtp.timeout =3000
JavaMailSenderImpl sender=new JavaMailSenderImpl();
sender.setPassword("") //该密码是授权码,而不是邮箱的登陆密码
启用授权码,避免密码泄漏造成邮箱安全隐患,使用授权码是可以访问邮箱的部分功能(发邮件)。使用授权码是无法登陆邮箱的.
开启授权码需要手机验证,对于163邮箱,如下 if ((useAuth || (user != null && password != null)) &&
(supportsExtension("AUTH") ||
supportsExtension("AUTH=LOGIN"))) {
if (logger.isLoggable(Level.FINE))
logger.fine("protocolConnect login" +
", host=" + host +
", user=" + traceUser(user) +
", password=" + tracePassword(password));
connected = authenticate(user, password); //校验用户名 和 授权码
如果有兴趣,可以自己看一下源码,我也是看的模模糊糊。
下面是SMTPTransport.class 的部分代码
//与服务器建立连接,并进行验证
@Override
protected synchronized boolean protocolConnect(String host, int port,
String user, String password)
throws MessagingException {
Properties props = session.getProperties();
// 是否需要校验
boolean useAuth = PropUtil.getBooleanProperty(props,
"mail." + name + ".auth", false);
//根据传入useAuth user和password的值进行判断
if (useAuth && (user == null || password == null)) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("need username and password for authentication");
logger.fine("protocolConnect returning false" +
", host=" + host +
", user=" + traceUser(user) +
", password=" + tracePassword(password));
}
return false;
}
//默认使用true
boolean useEhlo = PropUtil.getBooleanProperty(props,
"mail." + name + ".ehlo", true);
if (logger.isLoggable(Level.FINE))
logger.fine("useEhlo " + useEhlo + ", useAuth " + useAuth);
//设置默认的host和port
if (port == -1)
port = PropUtil.getIntProperty(props,
"mail." + name + ".port", -1);
if (port == -1)
port = defaultPort;
if (host == null || host.length() == 0)
host = "localhost";
boolean connected = false;
try {
//..........开启服务
boolean succeed = false;
if (useEhlo)
succeed = ehlo(getLocalHost());//收集服务器的扩展参数
if (!succeed)
helo(getLocalHost());//
//................
//即便useAuth是false,如果扩展列表的值为true,也会进行验证。这跟邮件服务器的设置有关
if ((useAuth || (user != null && password != null)) &&
(supportsExtension("AUTH") ||
supportsExtension("AUTH=LOGIN"))) {
if (logger.isLoggable(Level.FINE))
logger.fine("protocolConnect login" +
", host=" + host +
", user=" + traceUser(user) +
", password=" + tracePassword(password));
connected = authenticate(user, password); //校验用户名 和 授权码
return connected;
}
//..............异常捕获等代码
}
supportsExtension 方法,就是从extMap这个HashTable中获取数据,extMap中存放的数据是在ehlo方法中添加的
// private Hashtable extMap;
public boolean supportsExtension(String ext) {
return extMap != null &&
extMap.get(ext.toUpperCase(Locale.ENGLISH)) != null;
}
与服务器进行连接时,会执行ehlo,获取服务器的服务扩展列表。
protected boolean ehlo(String domain) throws MessagingException {
String cmd;
if (domain != null)
cmd = "EHLO " + domain;
else
cmd = "EHLO";
sendCommand(cmd);
int resp = readServerResponse();
if (resp == 250) {
// extract the supported service extensions
BufferedReader rd =
new BufferedReader(new StringReader(lastServerResponse));
String line;
extMap = new Hashtable<>();
try {
boolean first = true;
while ((line = rd.readLine()) != null) {
if (first) { // skip first line which is the greeting
first = false;
continue;
}
if (line.length() < 5)
continue; // shouldn't happen
line = line.substring(4); // skip response code
int i = line.indexOf(' ');
String arg = "";
if (i > 0) {
arg = line.substring(i + 1);
line = line.substring(0, i);
}
if (logger.isLoggable(Level.FINE))
logger.fine("Found extension "" +
line + "", arg "" + arg + """);
extMap.put(line.toUpperCase(Locale.ENGLISH), arg); //存储获取的扩展列表
}
} catch (IOException ex) { } // can't happen
}
return resp == 250;
}
public synchronized void connect(String host, int port,
String user, String password) throws MessagingException {
//省略其他。。。.。。。。。。。。
if (!connected) {
if (authEx != null)
throw authEx;
else if (user == null)
throw new AuthenticationFailedException(
"failed to connect, no user name specified?");
else if (password == null)
throw new AuthenticationFailedException(
"failed to connect, no password specified?");
else
throw new AuthenticationFailedException("failed to connect");
}
}
MimeMessage message = mailSender.createMimeMessage();
message .setFrom("666666@163.com"); //必须和设置的userName一致
MimeMessage mimeMessage=new MimeMessage ();
mimeMessage.setFrom( new InternetAddress("666666@163.com", "xxx"));
MimeUtility.encodeWord(fileName)