Java应用通过Gmail发邮件遇认证失败,求谷歌新规下的实现方案
解决Gmail第三方应用登录认证失败问题
谷歌自2022年5月30日起,已彻底禁用低安全应用访问权限,同时不再支持仅通过用户名+密码的方式登录谷歌账户的第三方应用/设备,这就是你遇到535-5.7.8 Username and Password not accepted认证错误的原因。目前有两种可靠的解决方案:
方案一:使用应用专用密码(个人场景首选)
这种方式操作简单,适合个人开发者使用,但前提是你的谷歌账户必须开启两步验证。
操作步骤:
- 登录谷歌账户,进入「安全」设置页面
- 在「登录谷歌」板块找到「应用专用密码」选项
- 选择应用类型为「邮件」,设备类型选「其他」(自定义名称即可),点击生成密码
- 将Java/Spring配置中原来的账户密码替换为这个生成的16位应用专用密码
Spring配置示例(application.properties):
spring.mail.host=smtp.gmail.com spring.mail.port=587 spring.mail.username=your-gmail@gmail.com spring.mail.password=生成的应用专用密码 spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true
方案二:OAuth 2.0认证(企业/多用户场景首选)
如果是面向多用户的应用,或者需要更高的安全性,推荐使用OAuth2认证方式,这也是谷歌官方推荐的第三方应用接入方式。
操作流程:
- 登录谷歌云平台,创建新的项目
- 在项目中启用「Gmail API」
- 创建OAuth客户端ID,应用类型根据你的场景选择:桌面应用/后端服务选「桌面应用」,Web应用选「Web应用」
- 下载客户端密钥文件(credentials.json),放置在项目资源目录中
- 通过授权流程获取刷新令牌,后续可自动刷新访问令牌
Spring整合OAuth2的代码示例:
首先添加Maven依赖:
<dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> <version>1.6.2</version> </dependency> <dependency> <groupId>com.google.oauth-client</groupId> <artifactId>google-oauth-client-java6</artifactId> <version>1.34.1</version> </dependency> <dependency> <groupId>com.google.apis</groupId> <artifactId>google-api-services-gmail</artifactId> <version>v1-rev110-1.25.0</version> </dependency>
然后编写配置类:
import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.gson.GsonFactory; import com.google.api.client.util.store.FileDataStoreFactory; import com.google.api.services.gmail.GmailScopes; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; import javax.mail.Session; import java.io.IOException; import java.io.InputStreamReader; import java.security.GeneralSecurityException; import java.util.Collections; import java.util.Properties; @Configuration public class GmailOAuth2Config { private static final String APP_NAME = "你的应用名称"; private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); private static final String TOKEN_STORAGE_PATH = "tokens"; private static final String CREDENTIALS_PATH = "/credentials.json"; private static final String SCOPE = GmailScopes.GMAIL_SEND; private Credential getOAuthCredential() throws IOException, GeneralSecurityException { var credentialStream = GmailOAuth2Config.class.getResourceAsStream(CREDENTIALS_PATH); if (credentialStream == null) { throw new IOException("未找到credentials.json文件"); } var clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(credentialStream)); var authFlow = new GoogleAuthorizationCodeFlow.Builder( GoogleNetHttpTransport.newTrustedTransport(), JSON_FACTORY, clientSecrets, Collections.singletonList(SCOPE)) .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKEN_STORAGE_PATH))) .setAccessType("offline") // 允许获取刷新令牌 .build(); var localReceiver = new LocalServerReceiver.Builder().setPort(8888).build(); return new AuthorizationCodeInstalledApp(authFlow, localReceiver).authorize("user"); } @Bean public JavaMailSender gmailSender() throws IOException, GeneralSecurityException { JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); mailSender.setHost("smtp.gmail.com"); mailSender.setPort(587); mailSender.setUsername("your-gmail@gmail.com"); Properties mailProps = mailSender.getJavaMailProperties(); mailProps.put("mail.smtp.auth", "true"); mailProps.put("mail.smtp.starttls.enable", "true"); mailProps.put("mail.smtp.auth.mechanisms", "XOAUTH2"); Session session = Session.getInstance(mailProps, new javax.mail.Authenticator() { @Override protected javax.mail.PasswordAuthentication getPasswordAuthentication() { try { String accessToken = getOAuthCredential().getAccessToken(); return new javax.mail.PasswordAuthentication("", accessToken); } catch (Exception e) { throw new RuntimeException("获取OAuth2令牌失败", e); } } }); mailSender.setSession(session); return mailSender; } }
注意事项:
- 应用专用密码仅适用于开启两步验证的账户,若关闭两步验证,该密码会立即失效
- OAuth2方式首次运行时会弹出浏览器授权页面,授权后会在本地生成
tokens目录存储刷新令牌,后续无需重复授权 - 谷歌云平台的项目必须启用Gmail API,否则会出现权限不足的错误
内容的提问来源于stack exchange,提问作者Shriram
相关产品推荐
相关产品推荐

