Получение исключения при отправке электронной почты через SMTP через сервер GMail с использованием OAUTH и JavaMail 1.4.7
Я относительно новичок в отправке электронной почты из приложения (в моем случае настольного приложения, которое я зарегистрировал в Google), и получаю следующее исключение (несмотря на неправильное написание "механизмов"):
javax.mail.AuthenticationFailedException: механизмы аутентификации не поддерживаются как сервером, так и клиентом.
Вот мой код, упрощенный и упакованный в один класс MailTest. Вывод отладки соответствует источнику. Есть идеи, что я делаю не так?
Мой тестовый класс:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Properties;
import java.util.stream.Collectors;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import com.google.gson.Gson;
public class MailTest {
private static String TOKEN_URL = "https://www.googleapis.com/oauth2/v4/token";
/* These constants are all encrypted and stored in my database. I've decrypted and added them here as literals */
private static final String APP_CLIENT_ID = "<my_app_client_id>apps.googleusercontent.com";
private static final String APP_CLIENT_SECRET = "<my_app_secret>";
private static final String REFRESH_TOKEN = "<my_app_refresh_token>";
private static String accessToken = "ya29.a0AfH6SMARliKqauH9m9mFktgNcKuTZs_c3bPak1yQsS0Uxethlo75NWSP2ad3uBAJWItM-kzY08n6lSaL931gbBaqJY7ZavsyA2y092MZiGdqBnEzXTbTNbvKomLvRoHupOquqTjf1KsrM70eFEWN9WMVL_dz";
/* This value is stored and retrieved from the database but setting to 0 here to force a refresh of "accessToken */
private static long tokenExpiration = 0;
private static final String FROM_EMAIL_ADDRESS = "<FromEmail>@gmail.com";
private static final String FROM_NAME = "<From Name>";
/* Hard coded list of recipients for simplicity */
private static String[] recipientNames = new String[] { "ToEmail@gmail.com" };
public static void main(String args[]) {
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.auth.mechanisms", "XOAUTH2");
props.put("mail.smtp.port", 587);
props.put("mail.debug", "true");
props.put("mail.debug.auth", "true");
try {
Session session = Session.getInstance(props);
MimeMessage msg = createMessage(session);
Transport transport = session.getTransport("smtp");
transport.connect("smtp.gmail.com", FROM_EMAIL_ADDRESS, getAccessToken());
msg.saveChanges();
transport.sendMessage(msg, msg.getAllRecipients());
transport.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static MimeMessage createMessage(Session session) throws UnsupportedEncodingException, MessagingException {
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(FROM_EMAIL_ADDRESS, FROM_NAME));
InternetAddress[] toAddresses = new InternetAddress[recipientNames.length];
for (int i = 0; i < recipientNames.length; i++) {
toAddresses[i] = new InternetAddress(recipientNames[i]);
}
msg.setRecipients(Message.RecipientType.TO, toAddresses);
msg.setSubject("My email subject", "UTF-8"); //$NON-NLS-1$
msg.setContent("Hello world!", "plain");
msg.setSentDate(new Date());
return msg;
}
/*
* Return the access token, renewing if expired
*/
private static String getAccessToken() throws IOException {
if (System.currentTimeMillis() > tokenExpiration) {
refreshAccessToken();
}
return accessToken;
}
// For parsing using json
private static class RefreshTokenReply {
private String access_token;
private String expires_in;
}
/*
* Renew access token
*/
private static void refreshAccessToken() throws IOException {
String data = "refresh_token=" + REFRESH_TOKEN + "&client_id=" + APP_CLIENT_ID
+ "&client_secret=" + APP_CLIENT_SECRET + "&grant_type=refresh_token";
URL url = new URL(TOKEN_URL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
byte[] body = data.getBytes();
conn.setDoOutput(true);
conn.setFixedLengthStreamingMode(body.length);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.getOutputStream().write(body);
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
String reply = reader.lines().collect(Collectors.joining(System.lineSeparator()));
System.out.println("Token refresh reply:" + System.lineSeparator() + reply);
Gson jsonParser = new Gson();
RefreshTokenReply refreshReply = jsonParser.fromJson(reply, RefreshTokenReply.class);
reader.close();
accessToken = refreshReply.access_token;
if (accessToken == null) {
throw new IOException("Refresh token yielded no access token");
}
tokenExpiration = System.currentTimeMillis() + Integer.parseInt(refreshReply.expires_in) * 1000;
}
}
Вот результат:
DEBUG: JavaMail version 1.4.7
DEBUG: successfully loaded resource: /META-INF/javamail.default.providers
DEBUG: Tables of loaded providers
DEBUG: Providers Listed By Class Name: {com.sun.mail.smtp.SMTPSSLTransport=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], com.sun.mail.smtp.SMTPTransport=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle], com.sun.mail.imap.IMAPSSLStore=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], com.sun.mail.pop3.POP3SSLStore=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle], com.sun.mail.imap.IMAPStore=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], com.sun.mail.pop3.POP3Store=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle]}
DEBUG: Providers Listed By Protocol: {imaps=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], imap=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], smtps=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], pop3=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle], pop3s=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle], smtp=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]}
DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
Token refresh reply:
{
"access_token": "ya29.a0AfH6SMBYeEk1DSDyS2pwBW0mNjOPUVVWqZjAOAD9CdWAB_a5229D9bZZXm123uQV20ZuQxyK38YytTGNDtnsuT8_HdS_y_PS4yzg_MNtC8jrfaYYGDiT0MNF87hE1sEaJmeSTzjsfc4OyFojbf1quILt1o-6XQ",
"expires_in": 3599,
"scope": "https://mail.google.com/",
"token_type": "Bearer"
}
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "smtp.gmail.com", port 587, isSSL false
220 smtp.gmail.com ESMTP x19sm6016872oor.45 - gsmtp
DEBUG SMTP: connected to host "smtp.gmail.com", port: 587
EHLO Ricks-Laptop
250-smtp.gmail.com at your service, [71.229.130.76]
250-SIZE 35882577
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "35882577"
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "STARTTLS", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "CHUNKING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
STARTTLS
220 2.0.0 Ready to start TLS
EHLO Ricks-Laptop
250-smtp.gmail.com at your service, [71.229.130.76]
250-SIZE 35882577
250-8BITMIME
250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "35882577"
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "AUTH", arg "LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH"
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "CHUNKING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
DEBUG SMTP: Attempt to authenticate using mechanisms: XOAUTH2
DEBUG SMTP: no authenticator for mechanism XOAUTH2
javax.mail.AuthenticationFailedException: No authentication mechansims supported by both server and client
at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:765)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:685)
at javax.mail.Service.connect(Service.java:295)
at javax.mail.Service.connect(Service.java:176)
at MailTest.main(MailTest.java:56)