Spring Boot集成Amazon SES发邮件报530 Authentication required错误
Spring Boot集成Amazon SES发邮件报530 Authentication required错误修复
问题现象
项目通过Amazon SES SMTP服务发送邮件时抛出异常:com.sun.mail.smtp.SMTPSendFailedException: 530 Authentication required
从调试日志可见关键信息:
- SMTP连接参数
useEhlo=true、useAuth=false - 已成功连接
email-smtp.ca-central-1.amazonaws.com的465 SSL端口 - 服务端返回支持
PLAIN、LOGIN认证方式 - 执行
MAIL FROM命令时被服务端拒绝,提示需要认证
对应调试日志与错误堆栈:
DEBUG SMTP: useEhlo true, useAuth false DEBUG SMTP: trying to connect to host "email-smtp.ca-central-1.amazonaws.com", port 465, isSSL true 220 email-smtp.amazonaws.com ESMTP SimpleEmailService-d-9VSVBLD64 0qSZgoJIAdLpaKOxPLSu DEBUG SMTP: connected to host "email-smtp.ca-central-1.amazonaws.com", port: 465 250-email-smtp.amazonaws.com 250-8BITMIME 250-AUTH PLAIN LOGIN 250 Ok DEBUG SMTP: Found extension "8BITMIME", arg "" DEBUG SMTP: Found extension "AUTH", arg "PLAIN LOGIN" DEBUG SMTP: Found extension "Ok", arg "" DEBUG SMTP: use8bit false MAIL FROM:example@mail.com 530 Authentication required com.sun.mail.smtp.SMTPSendFailedException: 530 Authentication required at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:1388) at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:959) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:583) at com.mit.webpaycdmx.email.EmailAmazonSES.send(EmailAmazonSES.java:118) at com.mit.webpaycdmx.email.EmailClient.sendEmail(EmailClient.java:54) at com.mit.webpaycdmx.services.impl.WebpayCdmxServiceImpl.sendEmail(WebpayCdmxServiceImpl.java:979) at com.mit.webpaycdmx.services.impl.WebpayCdmxServiceImpl.paymentResult(WebpayCdmxServiceImpl.java:307) at com.mit.webpaycdmx.controllers.WebpayCdmxController.paymentResult(WebpayCdmxController.java:146) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:207) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:998) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:901) at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:875) at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.filters.HttpHeaderSecurityFilter.doFilter(HttpHeaderSecurityFilter.java:126) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at com.mit.webpaycdmx.configuration.ClickjackingPreventionFilter.doFilter(ClickjackingPreventionFilter.java:18) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at com.mit.webpaycdmx.configuration.NoCacheFilter.doFilter(NoCacheFilter.java:42) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:543) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:688) at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:747) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:615) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:818) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1623) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:750)
原有配置与发送代码:
// 连接配置 properties.put("mail.transport.protocol", "smtps"; properties.put("mail.smtps.port", "465"); properties.put("mail.smtp.starttls.enable", "true"); properties.put("mail.smtp.auth", "true"); properties.put("mail.smtp.ssl.enable", "true"); properties.put("mail.debug", "true"); properties.put("mail.timeout", 5000); properties.put("mail.smtp.user", "someUser"); properties.put("mail.smtp.password", "somePassword"); // 邮件发送方法 public void send() throws MessagingException, IOException { Session session = Session.getDefaultInstance(properties, new javax.mail.Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(smtpUserName,smtpP4d); } }); session.setDebug(true); MimeMessage msg = new MimeMessage(session); msg.setFrom(new InternetAddress(from, fromName)); msg.setRecipient(Message.RecipientType.TO, new InternetAddress(to)); msg.setSubject(subject); Multipart multipart = new MimeMultipart(); BodyPart messageBodyPart = new MimeBodyPart(); messageBodyPart.setContent(body, "text/html"); multipart.addBodyPart(messageBodyPart); for (File attachment : attachments) { MimeBodyPart attachmentPart = new MimeBodyPart(); attachmentPart.attachFile(attachment); multipart.addBodyPart(attachmentPart); } msg.setContent(multipart); Transport transport = session.getTransport("smtps"); transport.connect(host, smtpUserName, smtpP4d); transport.sendMessage(msg, msg.getAllRecipients()); }
故障原因
- 核心问题:配置前缀与使用的传输协议不匹配
代码中指定使用smtps协议(对应465端口SSL直连模式),JavaMail会自动读取mail.smtps.*前缀下的配置参数,但你把认证、SSL、超时、账号等参数都配在了mail.smtp.*前缀下,运行时根本读不到mail.smtps.auth=true的配置,所以调试日志才会显示useAuth false,连接后跳过认证流程直接发MAIL FROM命令,被SES拒绝。 - 冗余配置干扰
mail.smtp.starttls.enable是587端口STARTTLS模式的配置项,465端口是原生SSL连接,不需要该参数,冗余配置可能引发协议协商异常。 - Session创建方法隐患
Session.getDefaultInstance是单例方法,如果JVM中已经存在默认Session实例,会直接返回旧实例,不会加载你新传入的配置和Authenticator,也可能导致配置失效。
修复步骤
- 统一所有SMTP配置的前缀为
mail.smtps.,删除无效的STARTTLS配置,修正后的配置如下:
properties.put("mail.transport.protocol", "smtps"); properties.put("mail.smtps.port", "465"); properties.put("mail.smtps.auth", "true"); properties.put("mail.smtps.ssl.enable", "true"); properties.put("mail.debug", "true"); properties.put("mail.smtps.timeout", 5000); properties.put("mail.smtps.user", "someUser"); properties.put("mail.smtps.password", "somePassword");
- 将
Session.getDefaultInstance替换为Session.getInstance,强制创建新的Session实例加载当前配置:
Session session = Session.getInstance(properties, new javax.mail.Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(smtpUserName,smtpP4d); } });
其余发送逻辑无需修改,重启服务后调试日志会显示useAuth true,连接后会先完成PLAIN/LOGIN认证再执行发信命令,530错误即可解决。
内容的提问来源于stack exchange,提问作者Eric Cuevas
相关产品推荐
相关产品推荐

