You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用javax.mail发送Gmail无送达及认证失败问题排查

问题描述

我用Kotlin开发Android应用,通过javax.mail包用Gmail账号发送邮件。调用sendEmail方法后邮件无送达,同时抛出javax.mail.AuthenticationFailedException: 535-5.7.8 Username and Password not accepted错误。想解决三个问题:

  1. 是否需要开启Gmail两步验证?
  2. 代码本身有没有问题?
  3. 如何修复这个认证失败的错误?

以下是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. 错误修复步骤

方案一:两步验证+应用专用密码(推荐)

  1. 登录Gmail账号,开启两步验证;
  2. 进入Google账号「安全」设置,生成应用专用密码(选择「邮件」应用,设备选「其他」);
  3. 将代码中的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 06:12:03