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


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

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

    1. package cn.edu.sgu.www.mhxysy.service.system.impl;
    2. import cn.edu.sgu.www.mhxysy.property.EmailProperties;
    3. import cn.edu.sgu.www.mhxysy.property.SystemSettingsProperties;
    4. import cn.edu.sgu.www.mhxysy.consts.RedisKeyPrefixConst;
    5. import cn.edu.sgu.www.mhxysy.dto.system.UserLoginDTO;
    6. import cn.edu.sgu.www.mhxysy.dto.system.UserUpdateDTO;
    7. import cn.edu.sgu.www.mhxysy.entity.system.User;
    8. import cn.edu.sgu.www.mhxysy.entity.system.UserLoginLog;
    9. import cn.edu.sgu.www.mhxysy.exception.GlobalException;
    10. import cn.edu.sgu.www.mhxysy.feign.FeignService;
    11. import cn.edu.sgu.www.mhxysy.redis.RedisRepository;
    12. import cn.edu.sgu.www.mhxysy.redis.StringRedisUtils;
    13. import cn.edu.sgu.www.mhxysy.restful.ResponseCode;
    14. import cn.edu.sgu.www.mhxysy.service.system.UserService;
    15. import cn.edu.sgu.www.mhxysy.util.IpUtils;
    16. import cn.edu.sgu.www.mhxysy.util.StringUtils;
    17. import cn.edu.sgu.www.mhxysy.util.UserUtils;
    18. import lombok.extern.slf4j.Slf4j;
    19. import org.apache.shiro.authc.UsernamePasswordToken;
    20. import org.apache.shiro.subject.Subject;
    21. import org.springframework.beans.factory.annotation.Autowired;
    22. import org.springframework.mail.javamail.JavaMailSender;
    23. import org.springframework.mail.javamail.MimeMessageHelper;
    24. import org.springframework.stereotype.Service;
    25. import javax.mail.MessagingException;
    26. import javax.mail.internet.MimeMessage;
    27. import java.time.LocalDateTime;
    28. import java.time.format.DateTimeFormatter;
    29. /**
    30. * @author heyunlin
    31. * @version 1.0
    32. */
    33. @Slf4j
    34. @Service
    35. public class UserServiceImpl implements UserService {
    36. private final FeignService feignService;
    37. private final JavaMailSender javaMailSender;
    38. private final EmailProperties emailProperties;
    39. private final RedisRepository redisRepository;
    40. private final StringRedisUtils stringRedisUtils;
    41. private final SystemSettingsProperties systemSettingsProperties;
    42. @Autowired
    43. public UserServiceImpl(
    44. FeignService feignService,
    45. JavaMailSender javaMailSender,
    46. EmailProperties emailProperties,
    47. RedisRepository redisRepository,
    48. StringRedisUtils stringRedisUtils,
    49. SystemSettingsProperties systemSettingsProperties) {
    50. this.feignService = feignService;
    51. this.javaMailSender = javaMailSender;
    52. this.emailProperties = emailProperties;
    53. this.redisRepository = redisRepository;
    54. this.stringRedisUtils = stringRedisUtils;
    55. this.systemSettingsProperties = systemSettingsProperties;
    56. }
    57. @Override
    58. public void logout() {
    59. // 删除角色的权限
    60. redisRepository.delete(UserUtils.getLoginUsername());
    61. // 注销
    62. UserUtils.getSubject().logout();
    63. }
    64. @Override
    65. public void login(UserLoginDTO loginDTO) {
    66. // 一、验证码判断
    67. // 得到用户输入的验证码
    68. String code = loginDTO.getCode();
    69. // 获取正确的验证码
    70. String uuid = loginDTO.getUuid();
    71. String key = RedisKeyPrefixConst.PREFIX_CAPTCHA + uuid;
    72. String realCode = stringRedisUtils.get(key);
    73. // 得到的验证码为空,则获取验证码到登录之间的时间已经过了3分钟,验证码过期已经被删除
    74. if (realCode == null) {
    75. throw new GlobalException(ResponseCode.BAD_REQUEST, "验证码已失效,请刷新页面重新获取~");
    76. }
    77. // 验证码校验
    78. if (!code.equalsIgnoreCase(realCode)) {
    79. throw new GlobalException(ResponseCode.BAD_REQUEST, "验证码错误~");
    80. }
    81. // 二、登录流程
    82. // 得到用户名
    83. String username = loginDTO.getUsername();
    84. log.debug("用户{}正在登录...", username);
    85. // 查询用户信息,如果用户被锁定,提前退出
    86. User user = feignService.selectByUsername(username);
    87. if (user != null) {
    88. if (user.getEnable()) {
    89. // 1、shiro登录认证
    90. UsernamePasswordToken token = new UsernamePasswordToken(username, loginDTO.getPassword());
    91. Subject subject = UserUtils.getSubject();
    92. subject.login(token);
    93. // 设置session失效时间:永不超时
    94. subject.getSession().setTimeout(-1001);
    95. // 2、修改管理员上一次登录时间
    96. User usr = new User();
    97. usr.setId(user.getId());
    98. usr.setLastLoginTime(LocalDateTime.now());
    99. feignService.updateById(usr);
    100. // 3、邮件通知
    101. if (emailProperties.isEnable()) {
    102. new Thread(() -> {
    103. // 定义日期格式
    104. DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
    105. MimeMessage message = javaMailSender.createMimeMessage();
    106. MimeMessageHelper helper = new MimeMessageHelper(message);
    107. try {
    108. String text = "您的账号" + username + "在广州登录了。" +
    109. "[" + LocalDateTime.now().format(formatter) + "]";
    110. helper.setFrom(emailProperties.getFrom());
    111. helper.setTo(emailProperties.getTo());
    112. helper.setText(text);
    113. javaMailSender.send(message);
    114. } catch (MessagingException e) {
    115. e.printStackTrace();
    116. }
    117. }).start();
    118. }
    119. // 4、如果开启了系统日志,添加管理员登录历史
    120. if (systemSettingsProperties.isLoginLog()) {
    121. UserLoginLog loginLog = new UserLoginLog();
    122. loginLog.setId(StringUtils.uuid());
    123. loginLog.setUserId(user.getId());
    124. loginLog.setLoginTime(LocalDateTime.now());
    125. loginLog.setLoginIp(IpUtils.getLocalHostAddress());
    126. loginLog.setLoginHostName(IpUtils.getLocalHostName());
    127. feignService.saveLoginLog(loginLog);
    128. }
    129. // 5、从redis中删除用户权限
    130. redisRepository.delete(username);
    131. // 6、查询用户的权限信息,并保存到redis
    132. redisRepository.save(username);
    133. } else {
    134. throw new GlobalException(ResponseCode.FORBIDDEN, "账号已被锁定,禁止登录!");
    135. }
    136. } else {
    137. throw new GlobalException(ResponseCode.NOT_FOUND, "用户名不存在~");
    138. }
    139. }
    140. @Override
    141. public void updatePass(UserUpdateDTO userUpdateDTO) {
    142. feignService.updatePass(userUpdateDTO);
    143. }
    144. }

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

    1. 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;
    2. nested exception is:
    3. 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;
    4. nested exception is:
    5. java.net.ConnectException: Connection timed out (Connection timed out); message exception details (1) are:
    6. Failed message 1:
    7. com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.163.com, 25; timeout -1;
    8. nested exception is:
    9. java.net.ConnectException: Connection timed out (Connection timed out)
    10. at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2210)
    11. at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:722)
    12. at javax.mail.Service.connect(Service.java:342)
    13. at org.springframework.mail.javamail.JavaMailSenderImpl.connectTransport(JavaMailSenderImpl.java:518)
    14. at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:437)
    15. at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:361)
    16. at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:356)
    17. at cn.edu.sgu.www.mhxysy.service.system.impl.UserServiceImpl.lambda$login$0(UserServiceImpl.java:135)
    18. at java.lang.Thread.run(Thread.java:745)
    19. Caused by: java.net.ConnectException: Connection timed out (Connection timed out)
    20. at java.net.PlainSocketImpl.socketConnect(Native Method)
    21. at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    22. at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    23. at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    24. at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    25. at java.net.Socket.connect(Socket.java:589)
    26. at java.net.Socket.connect(Socket.java:538)
    27. at com.sun.mail.util.SocketFetcher.createSocket(SocketFetcher.java:335)
    28. at com.sun.mail.util.SocketFetcher.getSocket(SocketFetcher.java:214)
    29. at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2160)
    30. ... 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连接的方式,在原来的邮件设置中加入以下设置

    1. spring:
    2. mail:
    3. port: 25
    4. host: smtp.163.com
    5. default-encoding: UTF-8
    6. username: xxxxx@163.com
    7. password: xxxxxxxxxxxxx
    8. # 以下是新增的设置
    9. properties:
    10. mail:
    11. debug: true
    12. smtp:
    13. auth: true
    14. ssl:
    15. trust: smtp.163.com
    16. starttls:
    17. enable: true
    18. required: true
    19. socketFactory:
    20. port: 465
    21. class: javax.net.ssl.SSLSocketFactory

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

  • 相关阅读:
    SpringBoot整合MyBatis和Redis
    Embedding
    vue-router4之路由的跳转
    bloaty
    0003Java安卓程序设计-springboot基于Android的学习生活交流APP
    双线性池化(Bilinear Pooling)
    双十一买哪款电容笔好?高性价比电容笔品牌排行榜
    【C++学习】类与对象(上)
    机器学习集成学习进阶Xgboost算法原理
    Transformer的PE(position embedding),即位置编码理解
  • 原文地址:https://blog.csdn.net/heyl163_/article/details/133468627