我正在运行一个非常基本的Javamail程序来尝试发送电子邮件。这是带main()的独立程序。一旦我开始工作,我计划在一个在tomcat下运行的servlet中使用Javamail。
运行此程序时,我收到AUTH LOGIN失败错误。我尝试了几种不同的属性设置,但都没有解决问题。
然后我在SO上发现了一条帖子,建议降低我的Google帐户所需的安全级别。当我降低安全设置时,身份验证成功。
当然,我立即回到Google帐户的更高安全级别。
我的问题是,如何让我的应用程序更安全,以便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();
}
}
答案 0 :(得分:5)
您可能想要使用OAuth2 authentication。
答案 1 :(得分:4)
我将我的解决方案作为单独的答案包括在内。我之前编辑过这个问题包括这个,但问题变得太长了。
下面显示的是一个servlet,它使用OAuth2从" Contact"发送电子邮件。在我的网站上形成。我按照比尔答案提供的链接提供了说明。
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 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);
}
}
<强> AccessTokenFromRefreshToken.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 - 代码使用未更改,已添加包语句
答案 2 :(得分:2)
如何让我的应用程序更安全,以便gmail不会 拒绝认证?
我认为一个好方法是启用 two-way authentication 并用生成的 application specific password <替换正常的Gmail密码您的代码中的/ em> 。
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());