使用javamail,gmail会因为应用程序不太安全而拒绝身份验证
我正在运行一个非常基本的Javamail程序来尝试发送电子邮件。这是带有main()的独立程序。一旦我让它工作起来,我计划在tomcat下运行的servlet中使用Javamail 运行此程序时,我收到“身份验证登录失败”错误。我尝试了几种不同的属性设置,但都没有解决问题 然后我在SO上找到一篇帖子,建议降低我谷歌账户的安全级别。当我降低安全设置时,身份验证成功 当然,我立即回到了谷歌账户的更高安全级别 我的问题是,如何使我的应用程序更安全,以便gmail不会拒绝身份验证 程序代码如下所示。该程序与许多其他Javamail问题中的代码非常相似 TryJavamail.java使用javamail,gmail会因为应用程序不太安全而拒绝身份验证,java,security,jakarta-mail,Java,Security,Jakarta Mail,我正在运行一个非常基本的Javamail程序来尝试发送电子邮件。这是带有main()的独立程序。一旦我让它工作起来,我计划在tomcat下运行的servlet中使用Javamail 运行此程序时,我收到“身份验证登录失败”错误。我尝试了几种不同的属性设置,但都没有解决问题 然后我在SO上找到一篇帖子,建议降低我谷歌账户的安全级别。当我降低安全设置时,身份验证成功 当然,我立即回到了谷歌账户的更高安全级别 我的问题是,如何使我的应用程序更安全,以便gmail不会拒绝身份验证 程序代码如下所示。该程
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();
}
}
/*
* 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);
}
}
/*
* 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; }
}
如何使我的应用程序更安全,使gmail不会
拒绝认证
在我看来,一个好办法是启用并用代码中生成的密码替换普通的Gmail密码
您可能想使用。我将我的解决方案作为一个单独的答案。我之前对问题进行了编辑,将其包括在内,但问题变得太长了 下面使用OAuth2身份验证的Servlet 下面显示的是一个servlet,它使用OAuth2从我网站上的“联系人”表单发送电子邮件。我按照比尔回答的链接中的说明进行操作 SendMessage.java(需要更复杂的实现,请阅读代码中的注释) AccessTokenFromRefreshToken.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();
}
}
/*
* 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);
}
}
/*
* 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语句您的代码包含多个。简单易懂!首先,您需要一个启用两步身份验证的google帐户,然后生成一个特定于应用程序的密码。您好,您能提供一个指向相同代码示例的链接吗?@abi_pat,我已经用实现OAuth2身份验证的代码更新了这个问题。希望有帮助。请注意,它只支持IMAP和SMTP,不支持POP3。当您使用Google oauth脚本生成凭据(oauth2.py)时,能否解释com.somedomain.oauth2.AccessTokenFromRefreshToken是什么。该脚本同时创建刷新令牌和访问令牌。访问令牌仅在1小时内有效。但是刷新令牌可以用来生成一个新的访问令牌。谢谢,但是我想知道这个导入是什么,你到底有什么?