|
|
import java.io.*;
|
|
|
import java.net.*;
|
|
|
import java.util.Base64;
|
|
|
import javax.net.ssl.*;
|
|
|
|
|
|
/**
|
|
|
* 真实的邮件发送器
|
|
|
* 使用原生Java实现SMTP协议发送邮件
|
|
|
*/
|
|
|
public class RealEmailSender {
|
|
|
|
|
|
private static final String SMTP_HOST = "smtp.qq.com";
|
|
|
private static final int SMTP_PORT = 587;
|
|
|
|
|
|
/**
|
|
|
* 发送邮件
|
|
|
*/
|
|
|
public static boolean sendEmail(String fromEmail, String fromPassword, String toEmail, String subject, String content) {
|
|
|
try {
|
|
|
// 1. 连接到SMTP服务器
|
|
|
Socket socket = new Socket(SMTP_HOST, SMTP_PORT);
|
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
|
|
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
|
|
|
|
|
|
// 2. 读取服务器欢迎信息
|
|
|
String response = reader.readLine();
|
|
|
System.out.println("服务器: " + response);
|
|
|
if (!response.startsWith("220")) {
|
|
|
throw new Exception("SMTP服务器连接失败");
|
|
|
}
|
|
|
|
|
|
// 3. 发送EHLO命令
|
|
|
writer.println("EHLO localhost");
|
|
|
response = readMultiLineResponse(reader);
|
|
|
System.out.println("EHLO响应: " + response);
|
|
|
|
|
|
// 4. 启动TLS加密
|
|
|
writer.println("STARTTLS");
|
|
|
response = reader.readLine();
|
|
|
System.out.println("STARTTLS响应: " + response);
|
|
|
if (!response.startsWith("220")) {
|
|
|
throw new Exception("启动TLS失败");
|
|
|
}
|
|
|
|
|
|
// 5. 升级到SSL连接
|
|
|
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
|
|
|
SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket, SMTP_HOST, SMTP_PORT, true);
|
|
|
sslSocket.startHandshake();
|
|
|
|
|
|
// 6. 重新创建读写器
|
|
|
reader = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
|
|
|
writer = new PrintWriter(sslSocket.getOutputStream(), true);
|
|
|
|
|
|
// 7. 重新发送EHLO
|
|
|
writer.println("EHLO localhost");
|
|
|
response = readMultiLineResponse(reader);
|
|
|
System.out.println("SSL EHLO响应: " + response);
|
|
|
|
|
|
// 8. 认证
|
|
|
writer.println("AUTH LOGIN");
|
|
|
response = reader.readLine();
|
|
|
System.out.println("AUTH响应: " + response);
|
|
|
if (!response.startsWith("334")) {
|
|
|
throw new Exception("认证失败");
|
|
|
}
|
|
|
|
|
|
// 9. 发送用户名(Base64编码)
|
|
|
String encodedUsername = Base64.getEncoder().encodeToString(fromEmail.getBytes());
|
|
|
writer.println(encodedUsername);
|
|
|
response = reader.readLine();
|
|
|
System.out.println("用户名响应: " + response);
|
|
|
if (!response.startsWith("334")) {
|
|
|
throw new Exception("用户名认证失败");
|
|
|
}
|
|
|
|
|
|
// 10. 发送密码(Base64编码)
|
|
|
String encodedPassword = Base64.getEncoder().encodeToString(fromPassword.getBytes());
|
|
|
writer.println(encodedPassword);
|
|
|
response = reader.readLine();
|
|
|
System.out.println("密码响应: " + response);
|
|
|
if (!response.startsWith("235")) {
|
|
|
throw new Exception("密码认证失败: " + response);
|
|
|
}
|
|
|
|
|
|
// 11. 发送邮件
|
|
|
// MAIL FROM
|
|
|
writer.println("MAIL FROM:<" + fromEmail + ">");
|
|
|
response = reader.readLine();
|
|
|
System.out.println("MAIL FROM响应: " + response);
|
|
|
if (!response.startsWith("250")) {
|
|
|
throw new Exception("MAIL FROM失败");
|
|
|
}
|
|
|
|
|
|
// RCPT TO
|
|
|
writer.println("RCPT TO:<" + toEmail + ">");
|
|
|
response = reader.readLine();
|
|
|
System.out.println("RCPT TO响应: " + response);
|
|
|
if (!response.startsWith("250")) {
|
|
|
throw new Exception("RCPT TO失败");
|
|
|
}
|
|
|
|
|
|
// DATA
|
|
|
writer.println("DATA");
|
|
|
response = reader.readLine();
|
|
|
System.out.println("DATA响应: " + response);
|
|
|
if (!response.startsWith("354")) {
|
|
|
throw new Exception("DATA失败");
|
|
|
}
|
|
|
|
|
|
// 邮件头和内容
|
|
|
writer.println("From: " + fromEmail);
|
|
|
writer.println("To: " + toEmail);
|
|
|
writer.println("Subject: =?UTF-8?B?" + Base64.getEncoder().encodeToString(subject.getBytes("UTF-8")) + "?=");
|
|
|
writer.println("MIME-Version: 1.0");
|
|
|
writer.println("Content-Type: text/plain; charset=UTF-8");
|
|
|
writer.println("Content-Transfer-Encoding: base64");
|
|
|
writer.println("X-Mailer: MathLearningSystem");
|
|
|
writer.println("X-Priority: 1");
|
|
|
writer.println();
|
|
|
writer.println(Base64.getEncoder().encodeToString(content.getBytes("UTF-8")));
|
|
|
writer.println(".");
|
|
|
|
|
|
response = reader.readLine();
|
|
|
System.out.println("邮件发送响应: " + response);
|
|
|
if (!response.startsWith("250")) {
|
|
|
throw new Exception("邮件发送失败");
|
|
|
}
|
|
|
|
|
|
// 12. 退出
|
|
|
writer.println("QUIT");
|
|
|
response = reader.readLine();
|
|
|
System.out.println("QUIT响应: " + response);
|
|
|
|
|
|
// 13. 关闭连接
|
|
|
sslSocket.close();
|
|
|
|
|
|
System.out.println("邮件发送成功!");
|
|
|
return true;
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
System.err.println("邮件发送失败: " + e.getMessage());
|
|
|
e.printStackTrace();
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 读取多行响应
|
|
|
*/
|
|
|
private static String readMultiLineResponse(BufferedReader reader) throws IOException {
|
|
|
StringBuilder response = new StringBuilder();
|
|
|
String line;
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
response.append(line).append("\n");
|
|
|
// 如果行不是以'-'结尾,说明是最后一行
|
|
|
if (line.length() >= 4 && line.charAt(3) != '-') {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
return response.toString();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 创建验证码邮件内容
|
|
|
*/
|
|
|
public static String createVerificationEmailContent(String verificationCode) {
|
|
|
return "您好!\n\n" +
|
|
|
"您正在注册数学学习软件,您的验证码是:\n\n" +
|
|
|
"验证码:" + verificationCode + "\n\n" +
|
|
|
"验证码有效期为10分钟,请及时使用。\n" +
|
|
|
"如果您没有注册我们的软件,请忽略此邮件。\n\n" +
|
|
|
"祝您学习愉快!\n" +
|
|
|
"数学学习软件团队\n\n" +
|
|
|
"此邮件由系统自动发送,请勿回复。";
|
|
|
}
|
|
|
}
|