使用javax.mail发送Gmail无送达及认证失败问题排查
问题描述
我用Kotlin开发Android应用,通过javax.mail包用Gmail账号发送邮件。调用sendEmail方法后邮件无送达,同时抛出javax.mail.AuthenticationFailedException: 535-5.7.8 Username and Password not accepted错误。想解决三个问题:
- 是否需要开启Gmail两步验证?
- 代码本身有没有问题?
- 如何修复这个认证失败的错误?
以下是Notify.kt完整代码:
package com.facecool.utils import android.graphics.Bitmap import java.io.ByteArrayOutputStream import java.util.Properties import javax.activation.DataHandler import javax.activation.DataSource import javax.mail.Authenticator import javax.mail.Message import javax.mail.Multipart import javax.mail.Part import javax.mail.PasswordAuthentication import javax.mail.Session; import javax.mail.Transport import javax.mail.internet.InternetAddress import javax.mail.internet.MimeBodyPart import javax.mail.internet.MimeMessage import javax.mail.internet.MimeMultipart import javax.mail.util.ByteArrayDataSource internal object Notify { fun sendEmail( subject: String?, message: String?, recipientEmail: String?, bmp: Bitmap? ) { if (recipientEmail == null || recipientEmail.isEmpty()) return try { val yourMail = "example@gmail.com" val yourPwd = "password" val properties = getProperties() properties["mail.debug"] = "false" // Create a session with authentication val session: Session = Session.getInstance(properties, object : Authenticator() { override fun getPasswordAuthentication(): PasswordAuthentication { return PasswordAuthentication( yourMail, yourPwd ) } }) val mimeMessage: MimeMessage? = createMimeMessage( session, yourMail, recipientEmail, subject, message, bmp!! ) Transport.send(mimeMessage) } catch (e: java.lang.Exception) { e.printStackTrace() } } fun getProperties(): Properties { val properties = Properties() properties.put("mail.smtp.host", "smtp.gmail.com") properties.put("mail.smtp.port", "587") properties.put("mail.smtp.starttls.enable", "true") properties.put("mail.smtp.auth", "true") properties.put("mail.smtp.ssl.trust", "smtp.gmail.com") return properties } private fun getImageData(bmp: Bitmap): ByteArray? { val byteArrayOutputStream = ByteArrayOutputStream() bmp.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) return byteArrayOutputStream.toByteArray() } fun createMimeMessage( session: Session?, yourMail: String, recipientEmail: String, subject: String?, message: String?, bmp: Bitmap ): MimeMessage? { try { val mimeMessage = MimeMessage(session) mimeMessage.setFrom(InternetAddress(yourMail)) if (!recipientEmail.isEmpty()) mimeMessage.setRecipient( Message.RecipientType.TO, InternetAddress(recipientEmail) ) mimeMessage.setSubject(subject) // Add text part to the multipart val multipart: Multipart = MimeMultipart("mixed") val textPart = MimeBodyPart() textPart.setText(message) multipart.addBodyPart(textPart) //add Recognized Image val filePart = MimeBodyPart() filePart.setFileName("Recognized Face.jpg") filePart.setDisposition(Part.ATTACHMENT) val source: DataSource = ByteArrayDataSource(getImageData(bmp), "image/jpeg") filePart.setDataHandler(DataHandler(source)) multipart.addBodyPart(filePart) //end mimeMessage.setContent(multipart) return mimeMessage } catch (e: Exception) { e.printStackTrace() } return null } }
问题解答
1. 是否需要开启Gmail两步验证?
不是强制要求,但更推荐开启:
- 开启两步验证后,必须使用「应用专用密码」登录SMTP,这比直接用账号密码更安全,且能避开Gmail对普通密码登录SMTP的限制;
- 如果不开启两步验证,Gmail的「不太安全的应用访问」选项已逐步淘汰,大部分账号无法通过普通密码登录SMTP。
2. 代码存在的问题
- 硬编码密码:直接写死
yourPwd = "password",既不安全,也不符合当前Gmail的认证规则; - 空值处理隐患:
bmp!!强制非空,调用时传入null会直接崩溃; - 调试日志关闭:
mail.debug=false关闭了SMTP交互日志,不利于排查认证细节; - 异常处理简陋:仅打印堆栈,没有针对认证失败的反馈逻辑;
- 空值未兜底:
subject、message未设置默认值,可能导致邮件内容异常。
3. 错误修复步骤
方案一:两步验证+应用专用密码(推荐)
- 登录Gmail账号,开启两步验证;
- 进入Google账号「安全」设置,生成应用专用密码(选择「邮件」应用,设备选「其他」);
- 将代码中的
yourPwd替换为这个应用专用密码。
方案二:代码修复建议
internal object Notify { fun sendEmail( subject: String?, message: String?, recipientEmail: String?, bmp: Bitmap? ) { // 提前拦截空值,避免后续崩溃 if (recipientEmail.isNullOrEmpty() || bmp == null) return try { val yourMail = "your-real-email@gmail.com" // 替换为生成的应用专用密码 val yourAppPassword = "your-app-specific-password" val properties = getProperties() properties["mail.debug"] = "true" // 开启调试日志,方便排查问题 val session: Session = Session.getInstance(properties, object : Authenticator() { override fun getPasswordAuthentication(): PasswordAuthentication { return PasswordAuthentication(yourMail, yourAppPassword) } }) // 空值判断,避免空指针 createMimeMessage(session, yourMail, recipientEmail, subject, message, bmp)?.let { Transport.send(it) } } catch (e: Exception) { e.printStackTrace() // 可添加错误回调,通知上层逻辑认证失败 } } fun getProperties(): Properties { val properties = Properties() properties.put("mail.smtp.host", "smtp.gmail.com") properties.put("mail.smtp.port", "587") properties.put("mail.smtp.starttls.enable", "true") properties.put("mail.smtp.auth", "true") properties.put("mail.smtp.ssl.trust", "smtp.gmail.com") return properties } // 去掉可空返回,因为bmp已经提前做非空判断 private fun getImageData(bmp: Bitmap): ByteArray { val byteArrayOutputStream = ByteArrayOutputStream() bmp.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) return byteArrayOutputStream.toByteArray() } fun createMimeMessage( session: Session?, yourMail: String, recipientEmail: String, subject: String?, message: String?, bmp: Bitmap ): MimeMessage? { return try { val mimeMessage = MimeMessage(session) mimeMessage.setFrom(InternetAddress(yourMail)) mimeMessage.setRecipient(Message.RecipientType.TO, InternetAddress(recipientEmail)) // 给subject设置默认值,避免空指针 mimeMessage.subject = subject ?: "无主题" val multipart: Multipart = MimeMultipart("mixed") val textPart = MimeBodyPart() // 给message设置默认值 textPart.setText(message ?: "") multipart.addBodyPart(textPart) val filePart = MimeBodyPart() filePart.fileName = "Recognized Face.jpg" filePart.disposition = Part.ATTACHMENT val source: DataSource = ByteArrayDataSource(getImageData(bmp), "image/jpeg") filePart.dataHandler = DataHandler(source) multipart.addBodyPart(filePart) mimeMessage.setContent(multipart) mimeMessage } catch (e: Exception) { e.printStackTrace() null } } }
额外注意事项
- 检查设备网络是否能访问Gmail的SMTP服务器(端口587是否被防火墙/代理拦截);
- 开启
mail.debug=true后,查看控制台输出的SMTP交互日志,确认认证失败的具体细节; - 确保Gmail账号没有开启严格的安全浏览限制。
内容的提问来源于stack exchange,提问作者saas
相关产品推荐
相关产品推荐

