前情简介
当服务器不能访问外网,又需要向外发送邮件(使用外部邮箱服务器)。
准备工作
需要先准备好环境:
- 能够发送邮件的服务器,邮箱账号,邮箱密码;
- 能正常访问的 socks5服务:ip,端口号,用户名,密码;
- 开发环境:jdk1.7,javax.mail-1.4.5.jar;
代码示例
package com.limbo;
import java.security.GeneralSecurityException;
import java.util.Date;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import com.sun.mail.util.MailSSLSocketFactory;
/**
* MailSendSocks.java
*
* @author: limbo
* @Date: 2020年6月14日
* @version V1.0
*/
public class MailSendSocks {
// SMTP邮箱服务器地址
public static final String MAIL_SMTP_HOST = "邮箱服务器地址";
// SMTP邮箱服务器端口
public static final String MAIL_SMTP_PORT = "邮箱服务器端口";
// SMTP邮箱账号
public static final String MAIL_ACCOUNT= "邮箱账号";
// SMTP邮箱密码
public static final String MAIL_PWD= "邮箱密码";
// 接收邮箱账号
public static final String MAIL_TO= "接收邮箱账号";
// socks IP 地址
public static final String SOCKS_HOST = "socks IP 地址";
// socks 端口
public static final String SOCKS_PORT = "socks 端口";
// socks用户名
public static final String SOCKS_USERNAME = "socks用户名";
// socks密码
public static final String SOCKS_PASSWORD = "socks密码";
public static void main(String[] args) {
try {
sendEmail();
} catch (Exception e) {
e.printStackTrace();
System.out.println("邮件发送失败!");
}
}
public static void sendEmail() throws MessagingException, GeneralSecurityException {
// 设置代理服务器
Properties props = System.getProperties();
props.setProperty("proxySet", "true");
props.setProperty("socksProxyHost", SOCKS_HOST);
props.setProperty("socksProxyPort", SOCKS_PORT);
// 设置发信邮箱的smtp服务器
props.setProperty("mail.smtp.host", MAIL_SMTP);
// 设置发信邮箱的smtp端口号
props.setProperty("mail.smtp.port", SMTP_PORT);
// 安全验证
props.put("mail.smtp.auth", "true");
// 打印日志
props.put("mail.debug", "true");
MyJavaAuthenticator authenticator = new MyJavaAuthenticator(SOCKS_USERNAME, SOCKS_PASSWORD);
java.net.Authenticator.setDefault(authenticator);
// 使用验证
Session session = Session.getDefaultInstance(props, new Authenticator() {
protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
return new javax.mail.PasswordAuthentication(MAIL_ACCOUNT, MAIL_PWD);
}
});
MimeMessage message = new MimeMessage(session);
Address address = new InternetAddress(MAIL_ACCOUNT);
Address toAaddress = new InternetAddress(MAIL_TO);
message.setFrom(address);
message.setRecipient(MimeMessage.RecipientType.TO, toAaddress);
message.setSubject("测试socks发送邮件");
message.setText("邮箱发送,设置java.net.Authenticator测试");
message.setSentDate(new Date());
// 发送邮件
Transport.send(message);
System.out.println("邮件发送成功!");
}
}
class MyAuthenticator extends Authenticator {
private String user = "";
private String password = "";
public MyAuthenticator(String user, String password) {
this.user = user;
this.password = password;
}
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password);
}
}
class MyJavaAuthenticator extends java.net.Authenticator {
private String user = "";
private String password = "";
public MyJavaAuthenticator(String user, String password) {
this.user = user;
this.password = password;
}
protected java.net.PasswordAuthentication getPasswordAuthentication() {
return new java.net.PasswordAuthentication(user, password.toCharArray());
}
}
注意的点:
- 使用System.getProperties()
- 配置proxySet、socksProxyHost、socksProxyPort
props.setProperty("proxySet", "true");
props.setProperty("socksProxyHost", SOCKS_HOST);
props.setProperty("socksProxyPort", SOCKS_PORT);
- 实现java.net.Authenticator权限验证,设置socks用户名密码
class MyJavaAuthenticator extends java.net.Authenticator {
private String user = "";
private String password = "";
public MyJavaAuthenticator(String user, String password) {
this.user = user;
this.password = password;
}
protected java.net.PasswordAuthentication getPasswordAuthentication() {
return new java.net.PasswordAuthentication(user, password.toCharArray());
}
}
容易引起误会的点
java.net.Authenticator和邮箱用户密码验证的的父类方法一致,导致误以为Authenticator是对socks进行验证。
实际上Authenticator是验证邮箱用户名密码的父类。