阿里云ECS服务器无法发送邮件问题解决方案

这篇文章分享一下自己把项目部署在阿里云ECS上之后,登录邮件提醒时的邮件发送失败问题,无法连接发送邮箱的服务器。

博主使用的springboot提供的发送邮件服务,如下所示,为了实现异步的效果,新开了一个线程来发送邮件。

java 复制代码
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);
	}

}

过了一段时间之后,后台打印出了连接邮箱服务器超时的日志。

java 复制代码
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是成功的

bash 复制代码
ping smtp.163.com

但是尝试访问25端口,却无响应

bash 复制代码
telnet smtp.163.com 25

于是在网上查找了一些解决方案,最后采用了通过ssl连接的方式,在原来的邮件设置中加入以下设置

bash 复制代码
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

最后重启服务,登陆的时候成功发出了邮件。

相关推荐
。puppy20 分钟前
HCIP--3实验- 链路聚合,VLAN间通讯,Super VLAN,MSTP,VRRPip配置,OSPF(静态路由,环回,缺省,空接口),NAT
运维·服务器
颇有几分姿色30 分钟前
深入理解 Linux 内存管理:free 命令详解
linux·运维·服务器
沈艺强32 分钟前
云计算答案
云计算
EricWang13581 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
算法与编程之美2 小时前
文件的写入与读取
linux·运维·服务器
Thanks_ks3 小时前
探索计算机互联网的奇妙世界:从基础到前沿的无尽之旅
物联网·云计算·区块链·tcp/ip协议·计算机互联网·万维网·未来科技
JaneJiazhao3 小时前
HTTPSOK:SSL/TLS证书自动续期工具
服务器·网络协议·ssl
IT技术分享社区3 小时前
C#实战:使用腾讯云识别服务轻松提取火车票信息
开发语言·c#·云计算·腾讯云·共识算法
萨格拉斯救世主4 小时前
戴尔R930服务器增加 Intel X710-DA2双万兆光口含模块
运维·服务器
无所谓จุ๊บ4 小时前
树莓派开发相关知识十 -小试服务器
服务器·网络·树莓派