java - 在这个 Java 程序上工作以了解 SMTP 的工作原理(不允许使用 JavaMail)
问题描述
我不确定如何将编码的 base64 凭据传递到邮件服务器。不使用 JavaMail 有什么办法吗?我一直遇到 authentication 530 authentication required 错误,我知道这是未正确发送凭据的结果,但我无法弄清楚这一点。
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.util.Base64;
import java.util.Base64.Encoder;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.net.*;
public class EmailClient
{
public static void main(String[] args) throws Exception
{
// Establish a TCP connection with the mail server.
SSLSocket socket = (SSLSocket) ((SSLSocketFactory)
SSLSocketFactory.getDefault()).
createSocket(InetAddress.getByName("smtp.gmail.com"), 465);
/* encoding the username and password in Base64 for SMTP */
Encoder base64Encoder = Base64.getEncoder();
String smtpUser =
base64Encoder.encodeToString("username".getBytes());
String smtpPass =
base64Encoder.encodeToString("password".getBytes());
// Create a BufferedReader to read a line at a time.
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
// Read greeting from the server.
String response = br.readLine();
System.out.println(response);
if (!response.startsWith("220")) {
throw new Exception("220 reply not received from server.");
}
// Get a reference to the socket's output stream.
OutputStream os = socket.getOutputStream();
// Send HELO command and get server response.
String command = "HELO alice\r\n";
System.out.print(command);
os.write(command.getBytes("US-ASCII"));
response = br.readLine();
System.out.println(response);
if (!response.startsWith("250")) {
throw new Exception("250 reply not received from server.");
}
// Authentication
String cmmd authMsg = "AUTH LOGIN\r\n"
// How do I send my encoded credentials to the server?
// Send MAIL FROM command.
String mailFrom = "MAIL FROM: <xxxxxxxx@gmail.com>\r\n";
System.out.print(mailFrom);
os.write(mailFrom.getBytes("US-ASCII"));
response = br.readLine();
System.out.println(response);
if (!response.startsWith("250")) {
socket.close();
throw new Exception("250 reply not received from server.");
}
// Send RCPT TO command.
String commandRCPT = "RCPT TO:<xxxxxxxxxx.com>\r\n";
System.out.print(commandRCPT);
os.write(commandRCPT.getBytes("US-ASCII"));
response = br.readLine();
System.out.println(response);
if (!response.startsWith("250")) {
socket.close();
throw new Exception("250 reply not received from server.");
}
// Send DATA command.
String commandDATA = "DATA\r\n";
System.out.print(commandDATA);
os.write(commandDATA.getBytes("US-ASCII"));
response = br.readLine();
System.out.println(response);
if (!response.startsWith("354")) {
socket.close();
throw new Exception("354 reply not received from server.");
}
// Send message data.
String msgLine1 = "email sent\r\n";
System.out.print(msgLine1);
os.write(msgLine1.getBytes("US-ASCII"));
// End with line with a single period.
String msgLine2 = ".\r\n";
System.out.print(msgLine2);
os.write(msgLine2.getBytes("US-ASCII"));
response = br.readLine();
System.out.println(response);
if (!response.startsWith("250")) {
socket.close();
throw new Exception("250 reply not received from server.");
}
// Send QUIT command.
String commandQUIT = "QUIT\r\n";
System.out.print(commandQUIT);
os.write(commandQUIT.getBytes("US-ASCII"));
response = br.readLine();
System.out.println(response);
if (!response.startsWith("221")) {
socket.close();
throw new Exception("221 reply not received from server.");
}
socket.close();
}
}
解决方案
您显示的代码中没有进行SMTP 身份验证,这就是RCPT TO
命令失败的原因。请参阅SMTP AUTH 需要什么?.
您的 SMTP 客户端需要AUTH
在连接到服务器、识别自身以及发送任何 // 命令之前发送MAIL FROM
一个RCPT TO
成功的DATA
命令。您需要使用EHLO
notHELO
来确定服务器实际支持的AUTH
方案(LOGIN
、PLAIN
、GSSAPI
、DIGEST-MD5
、CRAM-MD5
、OAUTHBEARER
等),然后使用其中之一进行相应的身份验证1。
1:仅供参考,mail.smtp2go.com
支持CRAM-MD5
、PLAIN
和LOGIN
auth 方案。请参阅更详细地描述它们的参考资料。
有关详细信息,请参阅RFC 4422:简单身份验证和安全层 (SASL)、RFC 4954:用于身份验证的 SMTP 服务扩展和其他相关 RFC。
我还建议您阅读RFC 5321:简单邮件传输协议以了解 SMTP 协议的一般工作原理,因为您现在拥有的代码在处理协议方面是不完整的,特别是在它如何读取服务器的响应方面(请参阅部分4.2:SMTP 回复)。
尝试更多类似的东西:
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.util.Base64;
import java.util.Base64.Encoder;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.net.*;
public class EmailClient
{
private SSLSocket socket;
private BufferedReader br;
private OutputStream os;
private String lastResponseText;
private void checkReplyCode(int replyCode, int expectedReplyCode)
{
if (replyCode != expectedReplyCode)
throw new Exception(lastResponseText);
}
private int checkReplyCodes(int replyCode, int expectedReplyCodes[])
{
if (expectedReplyCodes == null)
return replyCode;
for (int i = 0; i < expectedReplyCodes.length; ++i)
{
if (replyCode == expectedReplyCodes[i])
return replyCode;
}
throw new Exception(lastResponseText);
}
private int readResponse()
{
lastResponseText = "";
String line = br.readLine();
System.out.println(line);
lastResponseText = line;
if ((line.length() > 3) && (line[3] == '-'))
{
String prefix = line.substring(0, 4);
do
{
line = br.readLine();
System.out.println(line);
lastResponseText += ("\r\n" + line.substring(4));
}
while (line.startsWith(prefix));
}
return Integer.parseInt(lastResponseText.substring(0, 3));
}
private void readResponse(int expectedReplyCode)
{
checkReplyCode(readResponse(), expectedReplyCode);
}
private int readResponse(int expectedReplyCodes[])
{
return checkReplyCodes(readResponse(), expectedReplyCodes);
}
private void sendLine(String line)
{
System.out.println(line);
os.write((line + "\r\n").getBytes("US-ASCII"));
}
private int sendCommand(String command)
{
sendLine(command);
return readResponse();
}
private void sendCommand(String command, int expectedReplyCode)
{
sendLine(command);
readResponse(expectedReplyCode);
}
private int sendCommand(String command, int expectedReplyCodes[])
{
sendLine(command);
return readResponse(expectedReplyCodes);
}
private String stringAsBase64(String data)
{
return Base64.getEncoder().encodeToString(data.getBytes("UTF-8"));
}
public static void main(String[] args) throws Exception
{
// Establish a TCP connection with the mail server.
socket = (SSLSocket) ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(InetAddress.getByName("smtp.gmail.com"), 465);
// Create a BufferedReader to read a line at a time.
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// Get a reference to the socket's output stream.
os = socket.getOutputStream();
// Read greeting from the server.
readResponse(220);
// Send HELO command and get server response.
//sendCommand("HELO alice", 250);
sendCommand("EHLO alice", 250);
// Authentication
sendCommand("AUTH LOGIN", 334);
/* encoding the username and password in Base64 for SMTP */
if (sendCommand(stringAsBase64("username"), new int[]{235, 334}) == 334)
sendCommand(stringAsBase64("password"), 235);
// Send MAIL FROM command.
sendCommand("MAIL FROM: <xxxxxxxx@gmail.com>", 250);
// Send RCPT TO command.
sendCommand("RCPT TO:<xxxxxxxxxx.com>", 250);
// Send DATA command.
sendCommand("DATA", 354);
// Send message data.
sendLine("From: <xxxxxxxx@gmail.com>");
sendLine("To: <xxxxxxxxxx.com>");
sendLine("Subject: Test");
sendLine("");
sendLine("email sent");
// End with line with a single period.
sendCommand(".", 250);
// Send QUIT command.
sendCommand("QUIT", 221);
socket.close();
}
}
推荐阅读
- node.js - 如果不是表情符号,Node.js 会删除不和谐消息
- flutter - 我的 Flutter App 被 Apple Review 拒绝,说明此 5.2 知识产权 (5.2.3)
- bash - Bash - 如何检查字符串中的所有字母是否都是大写的
- python - 如何在python中创建一个类似nan的类
- python - 没有任何打印和其他问题的硒错误
- postgresql - Moodle设置:错误代码:missingconfigversion
- python - 如何解决使用 feature_engine 时遇到的 TypeError
- c++ - 在 C++ 中使用全局变量初始化
- python - 异常锁定行为听起来是有意的设计吗?
- unity3d - 长按按钮弹跳后如何使我的摇杆旋转?