使用Java通过Gmail发邮件报错,求2022年适配方案及异常原因
Java通过Gmail发送邮件问题解答
该代码在2022年能否正常使用?
不能直接使用。Google在2022年彻底关闭了「不太安全的应用程序访问」权限,原代码中直接用Gmail账户密码登录SMTP的方式已经失效。必须改用应用专用密码(需开启两步验证)或OAuth 2.0认证才能正常连接Gmail的SMTP服务器。
抛出SSLHandshakeException异常的原因
1. TLS协议版本不兼容
Gmail的SMTP服务器仅支持TLS 1.2及以上版本的加密协议,但你的Java环境可能默认禁用了这些高版本协议(比如JDK 8早期版本、JDK 7及以下,默认未启用TLS 1.2),导致SSL握手时无法找到合适的协议,触发该异常。
2. 认证方式失效(叠加问题)
即使解决了协议问题,原代码用普通账户密码登录的方式也会失败——因为Google已经取消了普通密码用于SMTP登录的权限,必须使用应用专用密码或OAuth 2.0。
修复方案
方案一:启用两步验证并生成应用专用密码
- 登录你的Gmail账户,开启两步验证(2FA)
- 在Google账户设置中生成一个应用专用密码(这个密码仅用于你的Java程序,不会泄露主账户密码)
- 将代码中的
PASSWORD替换为生成的应用专用密码
方案二:强制Java使用TLS 1.2协议
在代码的Properties配置中添加以下参数,指定SMTP使用TLS 1.2:
props.put("mail.smtp.ssl.protocols", "TLSv1.2"); props.put("mail.smtp.starttls.required", "true");
如果是旧版JDK,也可以修改JDK安装目录下的conf/security/java.security文件,找到jdk.tls.disabledAlgorithms项,移除其中的TLSv1.2(不推荐,会影响全局Java程序)。
方案三:升级JavaMail/Jakarta Mail版本
确保使用最新版本的邮件API(现在推荐使用Jakarta Mail,原JavaMail已迁移到Jakarta EE),旧版本可能对新TLS协议的支持不完善。
调整后的可运行代码示例
public class GmailTest { private static String USER_NAME = "your-username"; // 仅填@gmail.com前的部分 private static String APP_PASSWORD = "your-generated-app-password"; // 替换为应用专用密码 private static String RECIPIENT = "random.address@gmail.com"; public static void main(String[] args) { String from = USER_NAME + "@gmail.com"; // 补全完整邮箱地址 String pass = APP_PASSWORD; String[] to = { RECIPIENT }; String subject = "Java send mail example"; String body = "Welcome to JavaMail!"; sendFromGMail(from, pass, to, subject, body); } private static void sendFromGMail(String from, String pass, String[] to, String subject, String body) { Properties props = System.getProperties(); String host = "smtp.gmail.com"; props.put("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.starttls.required", "true"); props.put("mail.smtp.ssl.protocols", "TLSv1.2"); // 指定TLS版本 props.put("mail.smtp.host", host); props.put("mail.smtp.user", from); props.put("mail.smtp.password", pass); props.put("mail.smtp.port", "587"); props.put("mail.smtp.auth", "true"); // 使用Session.getInstance替代getDefaultInstance,避免复用旧配置 Session session = Session.getInstance(props, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(from, pass); } }); MimeMessage message = new MimeMessage(session); try { message.setFrom(new InternetAddress(from)); InternetAddress[] toAddress = new InternetAddress[to.length]; for( int i = 0; i < to.length; i++ ) { toAddress[i] = new InternetAddress(to[i]); } for( int i = 0; i < toAddress.length; i++) { message.addRecipient(Message.RecipientType.TO, toAddress[i]); } message.setSubject(subject); message.setText(body); Transport transport = session.getTransport("smtp"); transport.connect(host, from, pass); transport.sendMessage(message, message.getAllRecipients()); transport.close(); } catch (Exception e) { e.printStackTrace(); } } }
内容的提问来源于stack exchange,提问作者Maik
相关产品推荐
相关产品推荐

