0

Google App Engine 的电子邮件服务使用 AppEngine SDK 中包含的 JavaMail JAR。它特别要求我们不要使用 Oracle 的 JAR

但是为了使用 Google OAuth 2.0 通过 SMTP 发送电子邮件,我们需要 Oracle 的 JavaMail JAR。

关于如何实现这一目标的任何想法?我被困住了。我们软件的内部功能使用 AppEngine 邮件在我们的团队内部发送电子邮件,我们的客户需要 Google OAuth SMTP 代表他们的电子邮件地址发送邮件。

包含 Oracle 的 JAR 后,我在尝试使用普通 AppEngine 邮件时收到此异常:

javax.mail.SendFailedException: Send failure (com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 25; timeout -1 (java.net.SocketException: Permission denied: Attempt to access a blocked recipient without permission.))
at javax.mail.Transport.send(Transport.java:163)
at javax.mail.Transport.send(Transport.java:48)
at app.util.EmailUtil.sendMail_AppEngine(EmailUtil.java:948)

更新:这是我使用 OAuth 发送邮件的代码

        Properties props = new Properties();
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.starttls.required", "true");
        props.put("mail.smtp.sasl.enable", "false");
        Session session = Session.getInstance(props);
        session.setDebug(false);

        final URLName unusedUrlName = null;
        SMTPTransport transport = new SMTPTransport(session, unusedUrlName);
        transport.connect("smtp.gmail.com", 587, fromAddress, null);

 byte[] response = String.format("user=%s\1auth=Bearer %s\1\1", lFrom, oauthToken).getBytes();
        response = BASE64EncoderStream.encode(response);

        transport.issueCommand("AUTH XOAUTH2 " + new String(response),
                235);

        MimeMessage msg = new MimeMessage( session );

        msg.setFrom( new InternetAddress( fromAddress , fromName ) );
        msg.setRecipients( Message.RecipientType.TO , InternetAddress.parse( toAddress , false ) );

        msg.setSubject( emailSubject );
        MimeBodyPart htmlPart = new MimeBodyPart();
        Multipart multiPart = new MimeMultipart();

        htmlPart.setContent( "<html>email content</html>" , "text/html; charset=UTF-8" );
        multiPart.addBodyPart( htmlPart );
        msg.setContent( multiPart );
        msg.saveChanges();

        transport.sendMessage(msg, msg.getAllRecipients());

我希望有人在这里帮助我。

4

1 回答 1

0

这段代码对我有用:

import java.security.Provider;
import java.security.Security;
import java.util.Properties;
import java.util.logging.Logger;

import javax.mail.Session;
import javax.mail.URLName;

import com.sun.mail.gimap.GmailSSLStore;
import com.sun.mail.smtp.SMTPTransport;

/**
 * Performs OAuth2 authentication.
 * 
 * <p>
 * Before using this class, you must call {@code initialize} to install the
 * OAuth2 SASL provider.
 */
public class XOAuth2Authenticator {

    private static final Logger logger = Logger.getLogger(XOAuth2Authenticator.class.getName());

    public static final class OAuth2Provider extends Provider {
        private static final long serialVersionUID = 1L;

        public OAuth2Provider() {
            super("Google OAuth2 Provider", 1.0, "Provides the XOAUTH2 SASL Mechanism");
            put("SaslClientFactory.XOAUTH2", "be.vsko.davidgadget.server.gmail.OAuth2SaslClientFactory");
        }
    }

    /**
     * Installs the OAuth2 SASL provider. This must be called exactly once
     * before calling other methods on this class.
     */
    public static void initialize() {
        Security.addProvider(new OAuth2Provider());
    }

    /**
     * Connects and authenticates to an IMAP server with OAuth2. You must have
     * called {@code initialize}.
     * 
     * @param host
     *            Hostname of the imap server, for example
     *            {@code imap.googlemail.com}.
     * @param port
     *            Port of the imap server, for example 993.
     * @param userEmail
     *            Email address of the user to authenticate, for example
     *            {@code oauth@gmail.com}.
     * @param oauthToken
     *            The user's OAuth token.
     * @param debug
     *            Whether to enable debug logging on the IMAP connection.
     * 
     * @return An authenticated GmailSSLStore that can be used for IMAP operations.
     */
    public static GmailSSLStore connectToImap(String host, int port, String userEmail, String oauthToken, boolean debug)
            throws Exception {

        logger.info("connecting to IMAP for user " + userEmail);

        Properties props = new Properties();
        // props.put("mail.imaps.sasl.enable", "true");
        // props.put("mail.imaps.sasl.mechanisms", "XOAUTH2");
        props.put("mail.gimaps.sasl.enable", "true");
        props.put("mail.gimaps.sasl.mechanisms", "XOAUTH2");
        props.put(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP, oauthToken);
        props.put("mail.store.protocol", "gimaps");
        Session session = Session.getInstance(props);
        props = session.getProperties();
        // props.put("mail.imaps.sasl.enable", "true");
        // props.put("mail.imaps.sasl.mechanisms", "XOAUTH2");
        props.put("mail.gimaps.sasl.enable", "true");
        props.put("mail.gimaps.sasl.mechanisms", "XOAUTH2");
        props.put(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP, oauthToken);
        props.put("mail.store.protocol", "gimaps");
        session.setDebug(debug);

        final URLName unusedUrlName = null;
        final String emptyPassword = "";

        GmailSSLStore gmailStore = new GmailSSLStore(session, unusedUrlName);
        gmailStore.connect(host, port, userEmail, emptyPassword);

        logger.info("SUCCESS connecting to IMAP for user " + userEmail);

        return gmailStore;

        // IMAPSSLStore store = new IMAPSSLStore(session, unusedUrlName);
        // store.connect(host, port, userEmail, emptyPassword);
        // return store;
    }

    /**
     * Connects and authenticates to an SMTP server with OAuth2. You must have
     * called {@code initialize}.
     * 
     * @param host
     *            Hostname of the smtp server, for example
     *            {@code smtp.googlemail.com}.
     * @param port
     *            Port of the smtp server, for example 587.
     * @param userEmail
     *            Email address of the user to authenticate, for example
     *            {@code oauth@gmail.com}.
     * @param oauthToken
     *            The user's OAuth token.
     * @param debug
     *            Whether to enable debug logging on the connection.
     * 
     * @return An authenticated SMTPTransport that can be used for SMTP
     *         operations.
     */
    public static SMTPTransport connectToSmtp(Session session, String host, int port, String userEmail) throws Exception {

        logger.info("connecting to SMTP for user " + userEmail);

        final URLName unusedUrlName = null;
        SMTPTransport transport = new SMTPTransport(session, unusedUrlName);
        // If the password is non-null, SMTP tries to do AUTH LOGIN.
        final String emptyPassword = "";
        transport.connect(host, port, userEmail, emptyPassword);

        logger.info("SUCCESS connecting to SMTP for user " + userEmail);

        return transport;
    }

    public static Session getSmtpSession(String oauthToken, boolean debug) {
        Properties props = new Properties();
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.starttls.required", "true");
        props.put("mail.smtp.sasl.enable", "true");
        props.put("mail.smtp.sasl.mechanisms", "XOAUTH2");
        props.put(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP, oauthToken);
        Session session = Session.getInstance(props);
        props = session.getProperties();
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.starttls.required", "true");
        props.put("mail.smtp.sasl.enable", "true");
        props.put("mail.smtp.sasl.mechanisms", "XOAUTH2");
        props.put(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP, oauthToken);
        session.setDebug(debug);
        return session;
    }
}

有了这个,您可以:

XOAuth2Authenticator.initialize();
Session smtpSession = XOAuth2Authenticator.getSmtpSession(oauthToken, true);
SMTPTransport transport = XOAuth2Authenticator.connectToSmtp(smtpSession, "smtp.gmail.com", 587, email);

现在像往常一样使用交通工具。

PS:绕过 IMO 更简单的方法是使用新的 GMAIL api

于 2014-12-11T16:57:31.737 回答