这篇文章分享一下自己把项目部署在阿里云ECS上之后,登录邮件提醒时的邮件发送失败问题,无法连接发送邮箱的服务器。
博主使用的springboot提供的发送邮件服务,如下所示,为了实现异步的效果,新开了一个线程来发送邮件。
package cn.edu.sgu.www.mhxysy.service.system.impl;
import cn.edu.sgu.www.mhxysy.property.EmailProperties;
import cn.edu.sgu.www.mhxysy.property.SystemSettingsProperties;
import cn.edu.sgu.www.mhxysy.consts.RedisKeyPrefixConst;
import cn.edu.sgu.www.mhxysy.dto.system.UserLoginDTO;
import cn.edu.sgu.www.mhxysy.dto.system.UserUpdateDTO;
import cn.edu.sgu.www.mhxysy.entity.system.User;
import cn.edu.sgu.www.mhxysy.entity.system.UserLoginLog;
import cn.edu.sgu.www.mhxysy.exception.GlobalException;
import cn.edu.sgu.www.mhxysy.feign.FeignService;
import cn.edu.sgu.www.mhxysy.redis.RedisRepository;
import cn.edu.sgu.www.mhxysy.redis.StringRedisUtils;
import cn.edu.sgu.www.mhxysy.restful.ResponseCode;
import cn.edu.sgu.www.mhxysy.service.system.UserService;
import cn.edu.sgu.www.mhxysy.util.IpUtils;
import cn.edu.sgu.www.mhxysy.util.StringUtils;
import cn.edu.sgu.www.mhxysy.util.UserUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* @author heyunlin
* @version 1.0
*/
@Slf4j
@Service
public class UserServiceImpl implements UserService {
private final FeignService feignService;
private final JavaMailSender javaMailSender;
private final EmailProperties emailProperties;
private final RedisRepository redisRepository;
private final StringRedisUtils stringRedisUtils;
private final SystemSettingsProperties systemSettingsProperties;
@Autowired
public UserServiceImpl(
FeignService feignService,
JavaMailSender javaMailSender,
EmailProperties emailProperties,
RedisRepository redisRepository,
StringRedisUtils stringRedisUtils,
SystemSettingsProperties systemSettingsProperties) {
this.feignService = feignService;
this.javaMailSender = javaMailSender;
this.emailProperties = emailProperties;
this.redisRepository = redisRepository;
this.stringRedisUtils = stringRedisUtils;
this.systemSettingsProperties = systemSettingsProperties;
}
@Override
public void logout() {
// 删除角色的权限
redisRepository.delete(UserUtils.getLoginUsername());
// 注销
UserUtils.getSubject().logout();
}
@Override
public void login(UserLoginDTO loginDTO) {
// 一、验证码判断
// 得到用户输入的验证码
String code = loginDTO.getCode();
// 获取正确的验证码
String uuid = loginDTO.getUuid();
String key = RedisKeyPrefixConst.PREFIX_CAPTCHA + uuid;
String realCode = stringRedisUtils.get(key);
// 得到的验证码为空,则获取验证码到登录之间的时间已经过了3分钟,验证码过期已经被删除
if (realCode == null) {
throw new GlobalException(ResponseCode.BAD_REQUEST, "验证码已失效,请刷新页面重新获取~");
}
// 验证码校验
if (!code.equalsIgnoreCase(realCode)) {
throw new GlobalException(ResponseCode.BAD_REQUEST, "验证码错误~");
}
// 二、登录流程
// 得到用户名
String username = loginDTO.getUsername();
log.debug("用户{}正在登录...", username);
// 查询用户信息,如果用户被锁定,提前退出
User user = feignService.selectByUsername(username);
if (user != null) {
if (user.getEnable()) {
// 1、shiro登录认证
UsernamePasswordToken token = new UsernamePasswordToken(username, loginDTO.getPassword());
Subject subject = UserUtils.getSubject();
subject.login(token);
// 设置session失效时间:永不超时
subject.getSession().setTimeout(-1001);
// 2、修改管理员上一次登录时间
User usr = new User();
usr.setId(user.getId());
usr.setLastLoginTime(LocalDateTime.now());
feignService.updateById(usr);
// 3、邮件通知
if (emailProperties.isEnable()) {
new Thread(() -> {
// 定义日期格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
try {
String text = "您的账号" + username + "在广州登录了。" +
"[" + LocalDateTime.now().format(formatter) + "]";
helper.setFrom(emailProperties.getFrom());
helper.setTo(emailProperties.getTo());
helper.setText(text);
javaMailSender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
}).start();
}
// 4、如果开启了系统日志,添加管理员登录历史
if (systemSettingsProperties.isLoginLog()) {
UserLoginLog loginLog = new UserLoginLog();
loginLog.setId(StringUtils.uuid());
loginLog.setUserId(user.getId());
loginLog.setLoginTime(LocalDateTime.now());
loginLog.setLoginIp(IpUtils.getLocalHostAddress());
loginLog.setLoginHostName(IpUtils.getLocalHostName());
feignService.saveLoginLog(loginLog);
}
// 5、从redis中删除用户权限
redisRepository.delete(username);
// 6、查询用户的权限信息,并保存到redis
redisRepository.save(username);
} else {
throw new GlobalException(ResponseCode.FORBIDDEN, "账号已被锁定,禁止登录!");
}
} else {
throw new GlobalException(ResponseCode.NOT_FOUND, "用户名不存在~");
}
}
@Override
public void updatePass(UserUpdateDTO userUpdateDTO) {
feignService.updatePass(userUpdateDTO);
}
}
过了一段时间之后,后台打印出了连接邮箱服务器超时的日志。
Exception in thread "Thread-25" org.springframework.mail.MailSendException: Mail server connection failed; nested exception is com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.163.com, 25; timeout -1;
nested exception is:
java.net.ConnectException: Connection timed out (Connection timed out). Failed messages: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.163.com, 25; timeout -1;
nested exception is:
java.net.ConnectException: Connection timed out (Connection timed out); message exception details (1) are:
Failed message 1:
com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.163.com, 25; timeout -1;
nested exception is:
java.net.ConnectException: Connection timed out (Connection timed out)
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2210)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:722)
at javax.mail.Service.connect(Service.java:342)
at org.springframework.mail.javamail.JavaMailSenderImpl.connectTransport(JavaMailSenderImpl.java:518)
at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:437)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:361)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:356)
at cn.edu.sgu.www.mhxysy.service.system.impl.UserServiceImpl.lambda$login$0(UserServiceImpl.java:135)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.ConnectException: Connection timed out (Connection timed out)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at java.net.Socket.connect(Socket.java:538)
at com.sun.mail.util.SocketFetcher.createSocket(SocketFetcher.java:335)
at com.sun.mail.util.SocketFetcher.getSocket(SocketFetcher.java:214)
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2160)
... 8 more
原因是:Couldn't connect to host, port: smtp.163.com, 25
但是通过终端连接smtp.163.com是成功的
ping smtp.163.com
但是尝试访问25端口,却无响应
telnet smtp.163.com 25
于是在网上查找了一些解决方案,最后采用了通过ssl连接的方式,在原来的邮件设置中加入以下设置
spring:
mail:
port: 25
host: smtp.163.com
default-encoding: UTF-8
username: xxxxx@163.com
password: xxxxxxxxxxxxx
# 以下是新增的设置
properties:
mail:
debug: true
smtp:
auth: true
ssl:
trust: smtp.163.com
starttls:
enable: true
required: true
socketFactory:
port: 465
class: javax.net.ssl.SSLSocketFactory
最后重启服务,登陆的时候成功发出了邮件。