这篇文章分享一下自己把项目部署在阿里云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;@Autowiredpublic 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;}@Overridepublic void logout() {// 删除角色的权限redisRepository.delete(UserUtils.getLoginUsername());// 注销UserUtils.getSubject().logout();}@Overridepublic 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、查询用户的权限信息,并保存到redisredisRepository.save(username);} else {throw new GlobalException(ResponseCode.FORBIDDEN, "账号已被锁定,禁止登录!");}} else {throw new GlobalException(ResponseCode.NOT_FOUND, "用户名不存在~");}}@Overridepublic 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: 25host: smtp.163.comdefault-encoding: UTF-8username: xxxxx@163.compassword: xxxxxxxxxxxxx# 以下是新增的设置properties:mail:debug: truesmtp:auth: truessl:trust: smtp.163.comstarttls:enable: truerequired: truesocketFactory:port: 465class: javax.net.ssl.SSLSocketFactory
最后重启服务,登陆的时候成功发出了邮件。