首页 > 解决方案 > 在这个 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();
    }
}

标签: javasocketsemailsmtp

解决方案


您显示的代码中没有进行SMTP 身份验证,这就是RCPT TO命令失败的原因。请参阅SMTP AUTH 需要什么?.

您的 SMTP 客户端需要AUTH在连接到服务器、识别自身以及发送任何 // 命令之前发送MAIL FROM一个RCPT TO成功的DATA命令。您需要使用EHLOnotHELO来确定服务器实际支持的AUTH方案(LOGINPLAINGSSAPIDIGEST-MD5CRAM-MD5OAUTHBEARER等),然后使用其中之一进行相应的身份验证1

1:仅供参考,mail.smtp2go.com支持CRAM-MD5PLAINLOGINauth 方案。请参阅更详细地描述它们的参考资料。

有关详细信息,请参阅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();
    }
}

推荐阅读