当前位置: 首页 > 知识库问答 >
问题:

使用javamail,gmail由于应用程序不太安全而拒绝身份验证

娄利
2023-03-14

我正在运行一个非常基本的Javamail程序来尝试发送电子邮件。这是带有main()的独立程序。一旦我让它工作,我计划在tomcat下运行的servlet中使用Javamail。

运行此程序时,我收到身份验证登录失败错误。我尝试了几种不同的属性设置,但都没有解决问题。

然后我在SO上找到一篇帖子,建议降低我谷歌账户的安全级别。当我降低安全设置时,身份验证成功。

当然,我立即回到了谷歌账户的更高安全级别。

我的问题是,如何让我的应用程序更安全,这样gmail就不会拒绝身份验证?

程序代码如下所示。该程序与SO上许多其他Javamail问题中的代码非常相似。

TryJavamail.java

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.util.Properties;
import javax.mail.*;
import javax.mail.internet.*;

public class TryJavamail {

  public static void main(String args[]) throws MessagingException {

    String submitName = "John Doe";
    String submitEmail = "from@example.com";
    String submitMessage = "This is the message";

    Properties props = new Properties();
    props.put("mail.transport.protocol", "smtp");
    props.setProperty("mail.smtp.host", "smtp.gmail.com");
    props.setProperty("mail.smtp.auth", "true");
    props.setProperty("mail.smtp.ssl.enable", "true");
    props.setProperty("mail.smtp.port", "465");
    Session session = Session.getInstance(props, null);

    session.setDebug(true);

    Message message = new MimeMessage(session);
    message.setSubject("Message from myapp website submit");
    message.setText(submitName + "; " + submitMessage);

    Address toAddress = new InternetAddress(submitEmail);
    message.setRecipient(Message.RecipientType.TO, toAddress);

    Transport transport = session.getTransport("smtp");
    transport.connect("smtp.gmail.com", "---userid---", "---password---");
    transport.sendMessage(message, message.getAllRecipients());
    transport.close();
  }
}

共有3个答案

澹台星光
2023-03-14

如何使我的应用程序更安全,以便gmail不会拒绝身份验证?

在我看来,一个好方法是启用双向身份验证,并在代码中用生成的特定于应用程序的密码替换正常的Gmail密码。

final String smtpServer = "smtp.gmail.com";
final String userAccount = "****@gmail.com"; // Sender Account.
final String password = "****"; // Password -> Application Specific Password.
final String SOCKET_FACTORY = "javax.net.ssl.SSLSocketFactory";
final String smtpPort = "587";
final String PORT = "465";

final Properties props = new Properties();
props.put("mail.smtp.host", smtpServer);
props.put("mail.smtp.user", userAccount);
props.put("mail.smtp.password", password);
props.put("mail.smtp.port", smtpPort);
props.put("mail.smtp.auth", true);
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.debug", "false");
props.put("mail.smtp.socketFactory.port", PORT);
props.put("mail.smtp.socketFactory.class", SOCKET_FACTORY);
props.put("mail.smtp.socketFactory.fallback", "false");

Session session = Session.getInstance(props,
new javax.mail.Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(userAccount, password);
    }
});
MimeMessage mimeMessage = new MimeMessage(session);
final Address toAddress = new InternetAddress("****@outlook.com"); // toAddress
final Address fromAddress = new InternetAddress(userAccount);
mimeMessage.setContent("This is a test mail...", "text/html; charset=UTF-8");
mimeMessage.setFrom(fromAddress);
mimeMessage.setRecipient(javax.mail.Message.RecipientType.TO, toAddress);
mimeMessage.setSubject("Test Mail...");
Transport transport = session.getTransport("smtp");
transport.connect(smtpServer, userAccount, password);
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
酆恩
2023-03-14

我将我的解决方案作为一个单独的答案。我之前编辑过这个问题,将其包括在内,但问题太长了。

下面显示的是一个servlet,它使用OAuth2从我的网站上的“联系人”表单发送电子邮件。我按照比尔回答的链接中提供的说明进行操作。

SendMessage.java(需要更复杂的实现,请阅读代码中的注释)

/*
 * This program is adapted from sample code provided
 * by Google Inc at the following location:
 *          https://github.com/google/gmail-oauth2-tools
 *
 */

package com.somedomain.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.util.Properties;
import java.util.logging.Logger;

import javax.mail.Session;
import javax.mail.Message;
import javax.mail.Address;
import javax.mail.Transport;
import javax.mail.URLName;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.InternetAddress;

import java.security.Provider;
import java.security.Security;

import com.sun.mail.smtp.SMTPTransport;

import com.somedomain.oauth2.AccessTokenFromRefreshToken;
import com.somedomain.oauth2.OAuth2SaslClientFactory;

public class SendMessage extends HttpServlet {

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

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

        public OAuth2Provider() {
            super("Google OAuth2 Provider", 1.0,
                  "Provides the XOAUTH2 SASL Mechanism");
            put("SaslClientFactory.XOAUTH2",
                    "com.somedomain.oauth2.OAuth2SaslClientFactory");
        }
    }

    public static void initialize() {
        Security.addProvider(new OAuth2Provider());
    }

    public static SMTPTransport connectToSmtp(Session session,
                                            String host,
                                            int port,
                                            String userEmail,
                                            String oauthToken,
                                            boolean debug) throws Exception {

        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);

        return transport;
    }

    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response)
                          throws ServletException, IOException
    {
        String submitName = request.getParameter("name");
        String submitEmail = request.getParameter("email");
        String submitPhone = request.getParameter("phone");
        String submitMessage = request.getParameter("message");

        try {

            String host = "smtp.gmail.com";
            int    port = 587;
            String userEmail = "---email account used for oauth2---";
            String appEmail = "---email account for receiving app emails---";
            String oauthToken = "";

            initialize();

            //
            // Gmail access tokens are valid for 1 hour. A more sophisticated
            // implementation would store access token somewhere and reuse it
            // if it was not expired. A new access token should be generated
            // only if html" target="_blank">access token is expired. Abandoning unexpired access
            // tokens seems wasteful.
            //
            oauthToken = AccessTokenFromRefreshToken.getAccessToken();
            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);
            session.setDebug(true);

            SMTPTransport smtpTransport = connectToSmtp(session, host, port,
                                                userEmail, oauthToken, true);

            Message message = new MimeMessage(session);
            message.setSubject("Submit from somedomain.com website");
            message.setText("Name=" + submitName + "\n\nEmail=" + submitEmail +
                    "\n\nPhone=" + submitPhone + "\n\nMessage=" + submitMessage);


            Address toAddress = new InternetAddress(appEmail);
            message.setRecipient(Message.RecipientType.TO, toAddress);

            smtpTransport.sendMessage(message, message.getAllRecipients());
            smtpTransport.close();
        } catch (MessagingException e) {
            System.out.println("Messaging Exception");
            System.out.println("Error: " + e.getMessage());
        } catch (Exception e) {
            System.out.println("Messaging Exception");
            System.out.println("Error: " + e.getMessage());
        }

        String url = "/thankyou.html";
        response.sendRedirect(request.getContextPath() + url);
    }
}

oken.java

/*
 * For OAuth2 authentication, this program generates
 * access token from a previously acquired refresh token.
 */

package com.somedomain.oauth2;

import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
import java.util.LinkedHashMap;
import java.io.DataOutputStream;
import java.io.Reader;
import java.io.BufferedReader;
import java.io.InputStreamReader;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;


public class AccessTokenFromRefreshToken {

    public static String getAccessToken() {

        HttpURLConnection conn = null;
        String accessToken = null;

        try {

            URL url = new URL("https://accounts.google.com/o/oauth2/token");

            Map<String,Object> params = new LinkedHashMap<>();
            params.put("client_id", "***********.apps.googleusercontent.com");
            params.put("client_secret", "****************");
            params.put("refresh_token", "*****************");
            params.put("grant_type", "refresh_token");

            StringBuilder postData = new StringBuilder();
            for (Map.Entry<String,Object> param : params.entrySet()) {
                if (postData.length() != 0) postData.append('&');
                postData.append(URLEncoder.encode(param.getKey(), "UTF-8"));
                postData.append('=');
                postData.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
            }
            byte[] postDataBytes = postData.toString().getBytes("UTF-8");

            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

            conn.setRequestProperty("Content-Length",
                                String.valueOf(postDataBytes.length));
            conn.setRequestProperty("Content-language", "en-US");
            conn.setDoOutput(true);

            DataOutputStream wr = new DataOutputStream (
                            conn.getOutputStream());
            wr.write(postDataBytes);
            wr.close();

            StringBuilder sb = new StringBuilder();
            Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
            for ( int c = in.read(); c != -1; c = in.read() ) {
                sb.append((char)c);
            }

            String respString = sb.toString();

            // Read access token from json response
            ObjectMapper mapper = new ObjectMapper();
            AccessTokenObject accessTokenObj = mapper.readValue(respString,
                                                        AccessTokenObject.class);
            accessToken = accessTokenObj.getAccessToken();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(conn != null) {
                conn.disconnect(); 
            }
        }

        return(accessToken);
    }
}

AccessTokenObject。Java语言

/*
 * Class that corresponds to the JSON
 * returned by google OAuth2 token generator
 */

package com.somedomain.oauth2;

import com.fasterxml.jackson.annotation.JsonProperty;

public class AccessTokenObject {
    @JsonProperty("access_token")
    private String accessToken;

    @JsonProperty("token_type")
    private String tokenType;

    @JsonProperty("expires_in")
    private int expiresIn;

    public String getAccessToken() { return accessToken; }
    public String getTokenType() { return tokenType; }
    public int getExpiresIn() { return expiresIn; }

    public void setAccessToken(String accessToken) { this.accessToken = accessToken; }
    public void setTokenType(String tokenType) { this.tokenType = tokenType; }
    public void setExpiresIn(int expiresIn) { this.expiresIn = expiresIn; }
}

OAuth2SaslClient。java—使用的代码与gmail-oauth2-tools中的代码相同,只是在顶部添加了一个package语句(package com.somedomain.oauth2;)

OAuth2SaslClientFactory。java-未更改使用的代码,添加了package语句

傅明知
2023-03-14

您可能想使用OAuth2身份验证。

 类似资料:
  • 我写了一个从java(javamail/jaf)发送简单邮件的代码。运行该程序后,我收到一封来自google的电子邮件,表明我的帐户正被不安全的设备/应用程序访问。然后我不得不更改我的gmail帐户的设置以允许登录“不太安全的应用程序”选项。然后我收到了来自程序的电子邮件。 我需要在不更改选项的情况下发送电子邮件,允许在我的帐户中使用“不太安全的应用程序”选项。请帮忙。 我的代码是:

  • null 我正在尝试使用这个共享对话框在我自己的设备与我自己的Facebook帐户,这是应用程序的管理员以及。 在我的项目中: 项目名称:abddef app_name:Abd Def(在android manifest中)

  • 我正在编写一个简单的java程序,可以检查我的gmail收件箱。我在http://www.tutorialspoint.com/javamail_api/javamail_api_checking_emails.html.但是当我试着运行这个程序时 我得到以下错误 但是当我在我的gmail帐户中启用“访问不太安全的应用程序”时,这将完美运行。我如何解决这个问题。这个问题类似于只有当帐户启用了“访问

  • 问题内容: 我一直在做很多研究,找不到解决这个问题的方法。我正在尝试执行从https服务器到运行带有自定义自签名证书的码头的locahosthttps服务器的jQueryajax调用。我的问题是我无法确定响应是连接被拒绝还是响应不安全(由于缺少证书接受)。有没有办法确定两种情况之间的差异?的,并且总是在这两种情况下是相同的,即使在Chrome控制台我可以看到一个区别: 对于这两种情况,始终为“”,

  • 我正在尝试制作一种电子邮件客户端程序。我正在使用传输。connect尝试使用用户的用户名和密码连接到用户的Gmail帐户,但如果他们没有启用较不安全访问的设置,应用程序将返回此信息,我不希望必须这样做,因为不是每个用户都知道。我也不需要获取或发送邮件,我只需要获得有效的登录。

  • 我正在尝试使用Java向Gmail帐户发送邮件,代码如下。我似乎做的每件事都是正确的,但我收到了身份验证失败的消息。谷歌想让我打开“不太安全的应用”功能来实现传输。 有没有一种方法可以让Gmail对Java满意,并且不会出现“打开不太安全的应用程序”的错误? 错误: 代码: 我已经做了研究,所以据我所知,代码不是问题所在,只是没有看到一个解决方案,用于不太安全的应用程序消息。 参考文献: 参考文献