检验电子邮件地址是否真实存在
新项目,有需要提前“判断电子邮件地址是否真实存在”。首先想到这是一个标准化问题,网上肯定有参考答案了。大概思路是,发一封邮件给这个账户,或者通过SMTP等协议进行通信。邮箱几十亿,不可能有简单的API,直接判断是否有效,不然全网等可用邮箱都被你给拿到了。简单做个汇总,备忘。实践结论:QQ、163等标准化邮箱,可以判断是否存在。部分企业邮箱,如果使用的是腾讯企业邮箱,也可以...
新项目,有需要提前“判断电子邮件地址是否真实存在”。
首先想到这是一个标准化问题,网上肯定有参考答案了。
大概思路是,发一封邮件给这个账户,或者通过SMTP等协议进行通信。
邮箱几十亿,不可能有简单的API,直接判断是否有效,不然全网等可用邮箱都被你给拿到了。
简单做个汇总,备忘。
实践结论:QQ、163等标准化邮箱,可以判断是否存在。部分企业邮箱,如果使用的是腾讯企业邮箱,也可以。
部分企业邮箱,京东等,不太行。
微软Hotmail邮箱,返回超时。
在线检测工具,比如 https://verify-email.org/ 感觉不靠谱。
2037088@qq.com is BAD
从了解到放弃。
实际测试的邮箱,已做修改,仅供参考。
第1种实践过的,最佳代码:
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.MXRecord;
import org.xbill.DNS.Record;
import org.xbill.DNS.TextParseException;
import org.xbill.DNS.Type;
public class MailValidKit {
public static void main(String[] args) {
System.out.println("应该为true:"+new MailValidKit().valid("100582783@qq.com", "jootmir.org"));
System.out.println("应该为true:"+new MailValidKit().valid("fasuion@qq.com", "jootmir.org"));
System.out.println("应该为false:"+new MailValidKit().valid("fansnion@qq.com", "jootmir.org"));
System.out.println("应该为true:"+new MailValidKit().valid("lewenans@163.com", "jootmir.org"));
System.out.println("应该为true:"+new MailValidKit().valid("wen.lei@10cdit.com", "jootmir.org"));
System.out.println("应该为true:"+new MailValidKit().valid("ein1@jd.com", "jootmir.org"));
System.out.println("应该为true:"+new MailValidKit().valid("wngongao@jd.com", "jootmir.org"));
}
/**
* 验证邮箱是否存在
* <br>
* 由于要读取IO,会造成线程阻塞
*
* @param toMail
* 要验证的邮箱
* @param domain
* 发出验证请求的域名(是当前站点的域名,可以任意指定)
* @return
* 邮箱是否可达
*/
public boolean valid(String toMail, String domain) {
if(StringUtils.isBlank(toMail) || StringUtils.isBlank(domain)) return false;
if(!StringUtils.contains(toMail, '@')) return false;
String host = toMail.substring(toMail.indexOf('@') + 1);
if(host.equals(domain)) return false;
Socket socket = new Socket();
try {
// 查找mx记录
Record[] mxRecords = new Lookup(host, Type.MX).run();
if(ArrayUtils.isEmpty(mxRecords)) return false;
// 邮件服务器地址
String mxHost = ((MXRecord)mxRecords[0]).getTarget().toString();
if(mxRecords.length > 1) { // 优先级排序
List<Record> arrRecords = new ArrayList<Record>();
Collections.addAll(arrRecords, mxRecords);
Collections.sort(arrRecords, new Comparator<Record>() {
public int compare(Record o1, Record o2) {
return new CompareToBuilder().append(((MXRecord)o1).getPriority(), ((MXRecord)o2).getPriority()).toComparison();
}
});
mxHost = ((MXRecord)arrRecords.get(0)).getTarget().toString();
}
// 开始smtp
socket.connect(new InetSocketAddress(mxHost, 25));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(socket.getInputStream())));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
// 超时时间(毫秒)
long timeout = 6000;
// 睡眠时间片段(50毫秒)
int sleepSect = 50;
// 连接(服务器是否就绪)
if(getResponseCode(timeout, sleepSect, bufferedReader) != 220) {
return false;
}
// 握手
bufferedWriter.write("HELO " + domain + "\r\n");
bufferedWriter.flush();
if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
return false;
}
// 身份
bufferedWriter.write("MAIL FROM: <check@" + domain + ">\r\n");
bufferedWriter.flush();
if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
return false;
}
// 验证
bufferedWriter.write("RCPT TO: <" + toMail + ">\r\n");
bufferedWriter.flush();
if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
return false;
}
// 断开
bufferedWriter.write("QUIT\r\n");
bufferedWriter.flush();
return true;
} catch (NumberFormatException e) {
} catch (TextParseException e) {
} catch (IOException e) {
} catch (InterruptedException e) {
} finally {
try {
socket.close();
} catch (IOException e) {
}
}
return false;
}
private int getResponseCode(long timeout, int sleepSect, BufferedReader bufferedReader) throws InterruptedException, NumberFormatException, IOException {
int code = 0;
for(long i = sleepSect; i < timeout; i += sleepSect) {
Thread.sleep(sleepSect);
if(bufferedReader.ready()) {
String outline = bufferedReader.readLine();
// FIXME 读完……
while(bufferedReader.ready())
/*System.out.println(*/bufferedReader.readLine()/*)*/;
/*System.out.println(outline);*/
code = Integer.parseInt(outline.substring(0, 3));
break;
}
}
return code;
}
}
代码主要参考:https://www.cnblogs.com/Johness/p/javaemail_usesockettocheckemailaddressvalid.html
第2种,还可以,163邮箱无法识别
import java.io.IOException;
import org.apache.commons.net.smtp.SMTPClient;
import org.apache.commons.net.smtp.SMTPReply;
import org.apache.log4j.Logger;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.Record;
import org.xbill.DNS.Type;
/**
*
* 校验邮箱:1、格式是否正确 2、是否真实有效的邮箱地址
* 步骤: 1、从dns缓存服务器上查询邮箱域名对应的SMTP服务器地址 2、尝试建立Socket连接
* 3、尝试发送一条消息给SMTP服务器 4、设置邮件发送者 5、设置邮件接收者 6、检查响应码是否为250(为250则说明这个邮箱地址是真实有效的)
*
* @author Michael Ran
*
*/
// 总体靠谱
public class CheckEmailValidityUtil {
private static final Logger logger = Logger.getLogger(CheckEmailValidityUtil.class);
/**
* @param email
* 待校验的邮箱地址
* @return
*/
public static boolean isEmailValid(String email) {
if (!email.matches("[\\w\\.\\-]+@([\\w\\-]+\\.)+[\\w\\-]+")) {
logger.error("邮箱(" + email + ")校验未通过,格式不对!");
return false;
}
String host = "";
String hostName = email.split("@")[1];
// Record: A generic DNS resource record. The specific record types
// extend this class. A record contains a name, type, class, ttl, and
// rdata.
Record[] result = null;
SMTPClient client = new SMTPClient();
try {
// 查找DNS缓存服务器上为MX类型的缓存域名信息
Lookup lookup = new Lookup(hostName, Type.MX);
lookup.run();
if (lookup.getResult() != Lookup.SUCCESSFUL) {// 查找失败
logger.error("邮箱(" + email + ")校验未通过,未找到对应的MX记录!");
return false;
} else {// 查找成功
result = lookup.getAnswers();
}
// 尝试和SMTP邮箱服务器建立Socket连接
for (int i = 0; i < result.length; i++) {
host = result[i].getAdditionalName().toString();
logger.info("SMTPClient try connect to host:" + host);
// 此connect()方法来自SMTPClient的父类:org.apache.commons.net.SocketClient
// 继承关系结构:org.apache.commons.net.smtp.SMTPClient-->org.apache.commons.net.smtp.SMTP-->org.apache.commons.net.SocketClient
// Opens a Socket connected to a remote host at the current
// default port and
// originating from the current host at a system assigned port.
// Before returning,
// _connectAction_() is called to perform connection
// initialization actions.
// 尝试Socket连接到SMTP服务器
client.connect(host);
// Determine if a reply code is a positive completion
// response(查看响应码是否正常).
// All codes beginning with a 2 are positive completion
// responses(所有以2开头的响应码都是正常的响应).
// The SMTP server will send a positive completion response on
// the final successful completion of a command.
if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) {
// 断开socket连接
client.disconnect();
continue;
} else {
logger.info("找到MX记录:" + hostName);
logger.info("建立链接成功:" + hostName);
break;
}
}
logger.info("SMTPClient ReplyString:" + client.getReplyString());
//网易163,发现是自己等,有能力进行检测,所以最好用真实的
String emailPrefix = "leiwenfans";//leiwenfans222,可以随便写?
String emailSuffix = "163.com";
//emailPrefix="fansunion";
//emailSuffix="qq.com";
String fromEmail = emailPrefix + "@" + emailSuffix;
// Login to the SMTP server by sending the HELO command with the
// given hostname as an argument.
// Before performing any mail commands, you must first login.
// 尝试和SMTP服务器建立连接,发送一条消息给SMTP服务器
client.login(emailPrefix);
logger.info("SMTPClient login:" + emailPrefix + "...");
logger.info("SMTPClient ReplyString:" + client.getReplyString());
// Set the sender of a message using the SMTP MAIL command,
// specifying a reverse relay path.
// The sender must be set first before any recipients may be
// specified,
// otherwise the mail server will reject your commands.
// 设置发送者,在设置接受者之前必须要先设置发送者
client.setSender(fromEmail);
logger.info("设置发送者 :" + fromEmail);
logger.info("SMTPClient ReplyString:" + client.getReplyString());
// Add a recipient for a message using the SMTP RCPT command,
// specifying a forward relay path. The sender must be set first
// before any recipients may be specified,
// otherwise the mail server will reject your commands.
// 设置接收者,在设置接受者必须先设置发送者,否则SMTP服务器会拒绝你的命令
client.addRecipient(email);
logger.info("设置接收者:" + email);
logger.info("SMTPClient ReplyString:" + client.getReplyString());
logger.info("SMTPClient ReplyCode:" + client.getReplyCode() + "(250表示正常)");
if (250 == client.getReplyCode()) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
client.disconnect();
} catch (IOException e) {
}
}
return false;
}
//建立连接可能会非常慢,几十秒
public static void main(String[] args) {
//test163();
testJd();
//testBrng();
//testQq();
testHotmail();
}
//SMTPClient login:leiwenfans...
//SMTPClient ReplyString:250 DB5EUR03FT043.mail.protection.outlook.com Hello [36.110.19.90]
//没下文了
private static void testHotmail() {
System.out.println("合法hotmail邮箱:"+isEmailValid("fasnion@otmail.com"));
System.out.println("合法hotmail邮箱:"+isEmailValid("huingans@homail.com"));
}
//550
//设置接收者:liwe@jd.com
//SMTPClient ReplyString:550 5.1.1 Recipient address rejected: User unknown
private static void testJd() {
System.out.println("jd企业邮箱:"+isEmailValid("eien1@jd.com"));
}
private static void test163() {
//163总是false,550、503等异常状态,而不是250
System.out.println("合法163邮箱:"+isEmailValid("liwens@163.com"));
}
private static void testQq() {
//非法格式
System.out.println("格式不正确:"+isEmailValid("90310930qq.com"));
System.out.println("格式不正确2:"+isEmailValid("903109360@qqcom"));
//普通随机邮箱
System.out.println("普通随机邮箱:"+isEmailValid("903109360@qq.com"));//原作者自己测试等邮箱
System.out.println("普通随机邮箱,瞎填的:"+isEmailValid("903109360222@qq.com"));
//我的邮箱,qq
System.out.println("合法qq数字邮箱:"+isEmailValid("2437088@qq.com"));
System.out.println("合法qq自定义邮箱:"+isEmailValid("fasunon@qq.com"));
}
private static void testBaing() {
//baron企业邮箱ok
System.out.println("企业邮箱:"+isEmailValid("wn.li@10edit.com"));
System.out.println("企业邮箱2:"+isEmailValid("cheong.yu@1dit.com"));
System.out.println("企业邮箱3:"+isEmailValid("yonao.niu@10edit.com"));
System.out.println("企业邮箱4:"+isEmailValid("yonghao.@.com"));
System.out.println("企业邮箱:"+isEmailValid("wen.i123@100crdi"));
}
}
第3种,不太靠谱,io卡住,源代码中得正则表达式还有问题
import java.io.*;
import java.net.*;
import org.xbill.DNS.*;
//https://blog.csdn.net/CrazyGou/article/details/1649834 不太靠谱
public class CheckEmail
{
public static void main(String[] args) {
System.out.println("企业邮箱:"+CheckEmail.check("wen.lei@100ct.com"));//check方式有问题
System.out.println("qq邮箱:"+CheckEmail.check("2403818@qq.com"));//check方式有问题
}
public static boolean check(String mailTo)
{
//正则不对
/* if (!mailTo.matches("w+([-_.]w+)*@w+([-.]w+)*.w+([-.]w+)*")) { //判断格式
return false;
}*/
if (!mailTo.matches("[\\w\\.\\-]+@([\\w\\-]+\\.)+[\\w\\-]+")) {
System.out.println("邮箱(" + mailTo + ")校验未通过,格式不对!");
return false;
}
String hostName = mailTo.split("@")[1]; //获得如163.com
String host = null; // MX记录
try {
Lookup lookup = new Lookup(hostName, Type.MX);
lookup.run();
if (lookup.getResult() != Lookup.SUCCESSFUL) {
return false;
}
else {
Record[] result = lookup.getAnswers();
host = result[0].getAdditionalName().toString();
}
Socket socket = new Socket(host, 25);
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
in.readLine();
out.write("HELO Sender ");
out.flush();
in.readLine();
out.write("MAIL FROM:<xx@xxx.xx> ");
out.flush();
in.readLine();
out.write("RCPT TO:<" + mailTo + "> ");
out.flush();
String r = in.readLine();
out.write("QUIT ");
out.flush();
out.close();
in.close();
socket.close();
if (!r.startsWith("250")) {
return false;
}
else {
return true;
}
} catch (Exception e) {
return false;
}
}
}
----------------------以下内容纯copy,备用------------------------------------
1.Java与邮件系统交互之使用Socket验证邮箱是否存在
https://www.cnblogs.com/Johness/p/javaemail_usesockettocheckemailaddressvalid.html
Java与邮件系统交互之使用Socket验证邮箱是否存在
最近遇到一个需求:需要验证用户填写的邮箱地址是否真实存在,是否可达。和普通的正则表达式不同,他要求尝试链接目标邮箱服务器并请求校验目标邮箱是否存在。
先来了解
DNS之MX记录
对于DNS不了解的,请移步百度搜索。
DNS中除了A记录(域名-IP映射)之外,还有MX记录(邮件交换记录),CNAME记录(别名,咱不管)。
MX记录就是为了在发送邮件时使用友好域名规则,比如我们发送到QQ邮箱xxx@qq.com。我们填写地址是到“qq.com”,但实际上可能服务器地址千奇百怪/而且许有多个。在设置DNS时可以顺带设置MX记录。
说白了,“qq.com”只是域名,做HTTP请求响应地址,你邮件能发到Tomcat上吗?那我们发到“qq.com”上面的邮件哪里去了,我们把自己想象成一个邮件服务器,你的用户让你给xxx@qq.com发一封信,你如何操作?找mx记录是必要的。
SMTP之纯Socket访问
对于SMTP不了解或Java Socket不了解的,请移步百度搜索。
邮件协议是匿名协议,我们通过SMTP协议可以让邮件服务器来验证目标地址是否真实存在。
代码实现
由以上介绍可知:通过DNS中MX记录可以找到邮件服务器地址,通过SMTP协议可以让邮件服务器验证目标邮箱地址的真实性。
那么我们就来进行编码实现。
首先需要查询DNS,这个需要用到一个Java查询DNS的组件dnsjava(下载),自己写太麻烦。
mx
(上面代码中的生僻类型就是来自dnsjava,我使用apache-commons组件来判断空值和构建排序,return false是在查询失败时。)
接下来通过优先级排序(mx记录有这个属性)取第一个邮件服务器地址来链接。
这里的主要代码是通过SMTP发送RCPT TO指令来指定邮件接收方,如果这个地址存在则服务器返回成功状态,如果没有的话则返回错误指令。
1 import java.io.BufferedInputStream; 2 import java.io.BufferedReader; 3 import java.io.BufferedWriter; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.io.OutputStreamWriter; 7 import java.net.InetSocketAddress; 8 import java.net.Socket; 9 import java.util.ArrayList; 10 import java.util.Collections; 11 import java.util.Comparator; 12 import java.util.List; 13 14 import org.apache.commons.lang.ArrayUtils; 15 import org.apache.commons.lang.StringUtils; 16 import org.apache.commons.lang.builder.CompareToBuilder; 17 import org.xbill.DNS.Lookup; 18 import org.xbill.DNS.MXRecord; 19 import org.xbill.DNS.Record; 20 import org.xbill.DNS.TextParseException; 21 import org.xbill.DNS.Type; 22 23 24 public class MailValid { 25 26 public static void main(String[] args) { 27 System.out.println(new MailValid().valid("100582783@qq.com", "jootmir.org")); 28 } 29 30 /** 31 * 验证邮箱是否存在 32 * <br> 33 * 由于要读取IO,会造成线程阻塞 34 * 35 * @param toMail 36 * 要验证的邮箱 37 * @param domain 38 * 发出验证请求的域名(是当前站点的域名,可以任意指定) 39 * @return 40 * 邮箱是否可达 41 */ 42 public boolean valid(String toMail, String domain) { 43 if(StringUtils.isBlank(toMail) || StringUtils.isBlank(domain)) return false; 44 if(!StringUtils.contains(toMail, '@')) return false; 45 String host = toMail.substring(toMail.indexOf('@') + 1); 46 if(host.equals(domain)) return false; 47 Socket socket = new Socket(); 48 try { 49 // 查找mx记录 50 Record[] mxRecords = new Lookup(host, Type.MX).run(); 51 if(ArrayUtils.isEmpty(mxRecords)) return false; 52 // 邮件服务器地址 53 String mxHost = ((MXRecord)mxRecords[0]).getTarget().toString(); 54 if(mxRecords.length > 1) { // 优先级排序 55 List<Record> arrRecords = new ArrayList<Record>(); 56 Collections.addAll(arrRecords, mxRecords); 57 Collections.sort(arrRecords, new Comparator<Record>() { 58 59 public int compare(Record o1, Record o2) { 60 return new CompareToBuilder().append(((MXRecord)o1).getPriority(), ((MXRecord)o2).getPriority()).toComparison(); 61 } 62 63 }); 64 mxHost = ((MXRecord)arrRecords.get(0)).getTarget().toString(); 65 } 66 // 开始smtp 67 socket.connect(new InetSocketAddress(mxHost, 25)); 68 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(socket.getInputStream()))); 69 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); 70 // 超时时间(毫秒) 71 long timeout = 6000; 72 // 睡眠时间片段(50毫秒) 73 int sleepSect = 50; 74 75 // 连接(服务器是否就绪) 76 if(getResponseCode(timeout, sleepSect, bufferedReader) != 220) { 77 return false; 78 } 79 80 // 握手 81 bufferedWriter.write("HELO " + domain + "\r\n"); 82 bufferedWriter.flush(); 83 if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) { 84 return false; 85 } 86 // 身份 87 bufferedWriter.write("MAIL FROM: <check@" + domain + ">\r\n"); 88 bufferedWriter.flush(); 89 if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) { 90 return false; 91 } 92 // 验证 93 bufferedWriter.write("RCPT TO: <" + toMail + ">\r\n"); 94 bufferedWriter.flush(); 95 if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) { 96 return false; 97 } 98 // 断开 99 bufferedWriter.write("QUIT\r\n"); 100 bufferedWriter.flush(); 101 return true; 102 } catch (NumberFormatException e) { 103 } catch (TextParseException e) { 104 } catch (IOException e) { 105 } catch (InterruptedException e) { 106 } finally { 107 try { 108 socket.close(); 109 } catch (IOException e) { 110 } 111 } 112 return false; 113 } 114 115 private int getResponseCode(long timeout, int sleepSect, BufferedReader bufferedReader) throws InterruptedException, NumberFormatException, IOException { 116 int code = 0; 117 for(long i = sleepSect; i < timeout; i += sleepSect) { 118 Thread.sleep(sleepSect); 119 if(bufferedReader.ready()) { 120 String outline = bufferedReader.readLine(); 121 // FIXME 读完…… 122 while(bufferedReader.ready()) 123 /*System.out.println(*/bufferedReader.readLine()/*)*/; 124 /*System.out.println(outline);*/ 125 code = Integer.parseInt(outline.substring(0, 3)); 126 break; 127 } 128 } 129 return code; 130 } 131 }
(解锁和输出123、124行数据可以让你更加清晰SMTP协议)
对于企业邮箱,可能无法正常验证,这个是因为服务器问题。另外,dnsjava查询DNS是有缓存的。
2、Java验证邮箱有效性和真实性
https://blog.csdn.net/beagreatprogrammer/article/details/51150016
Java验证邮箱是否真实存在有效
要检测邮箱是否真实存在,必须了解两方面知识:
1. MX记录,winodws的nslookup命令。查看学习
2. SMTP协议,如何通过telnet发送邮件。查看学习
有个网站可以校验,http://verify-email.org/, 不过一小时只允许验证10次。
代码如下(补充了一些注释):
-
import java.io.IOException;
-
import org.apache.commons.net.smtp.SMTPClient;
-
import org.apache.commons.net.smtp.SMTPReply;
-
import org.apache.log4j.Logger;
-
import org.xbill.DNS.Lookup;
-
import org.xbill.DNS.Record;
-
import org.xbill.DNS.Type;
-
/**
-
*
-
* 校验邮箱:1、格式是否正确 2、是否真实有效的邮箱地址
-
* 步骤:
-
* 1、从dns缓存服务器上查询邮箱域名对应的SMTP服务器地址
-
* 2、尝试建立Socket连接
-
* 3、尝试发送一条消息给SMTP服务器
-
* 4、设置邮件发送者
-
* 5、设置邮件接收者
-
* 6、检查响应码是否为250(为250则说明这个邮箱地址是真实有效的)
-
* @author Michael Ran
-
*
-
*/
-
public class CheckEmailValidityUtil {
-
private static final Logger logger = Logger
-
.getLogger(CheckEmailValidityUtil.class);
-
/**
-
* @param email 待校验的邮箱地址
-
* @return
-
*/
-
public static boolean isEmailValid(String email) {
-
if (!email.matches("[\\w\\.\\-]+@([\\w\\-]+\\.)+[\\w\\-]+")) {
-
logger.error("邮箱("+email+")校验未通过,格式不对!");
-
return false;
-
}
-
String host = "";
-
String hostName = email.split("@")[1];
-
//Record: A generic DNS resource record. The specific record types
-
//extend this class. A record contains a name, type, class, ttl, and rdata.
-
Record[] result = null;
-
SMTPClient client = new SMTPClient();
-
try {
-
// 查找DNS缓存服务器上为MX类型的缓存域名信息
-
Lookup lookup = new Lookup(hostName, Type.MX);
-
lookup.run();
-
if (lookup.getResult() != Lookup.SUCCESSFUL) {//查找失败
-
logger.error("邮箱("+email+")校验未通过,未找到对应的MX记录!");
-
return false;
-
} else {//查找成功
-
result = lookup.getAnswers();
-
}
-
//尝试和SMTP邮箱服务器建立Socket连接
-
for (int i = 0; i < result.length; i++) {
-
host = result[i].getAdditionalName().toString();
-
logger.info("SMTPClient try connect to host:"+host);
-
-
//此connect()方法来自SMTPClient的父类:org.apache.commons.net.SocketClient
-
//继承关系结构:org.apache.commons.net.smtp.SMTPClient-->org.apache.commons.net.smtp.SMTP-->org.apache.commons.net.SocketClient
-
//Opens a Socket connected to a remote host at the current default port and
-
//originating from the current host at a system assigned port. Before returning,
-
//_connectAction_() is called to perform connection initialization actions.
-
//尝试Socket连接到SMTP服务器
-
client.connect(host);
-
//Determine if a reply code is a positive completion response(查看响应码是否正常).
-
//All codes beginning with a 2 are positive completion responses(所有以2开头的响应码都是正常的响应).
-
//The SMTP server will send a positive completion response on the final successful completion of a command.
-
if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) {
-
//断开socket连接
-
client.disconnect();
-
continue;
-
} else {
-
logger.info("找到MX记录:"+hostName);
-
logger.info("建立链接成功:"+hostName);
-
break;
-
}
-
}
-
logger.info("SMTPClient ReplyString:"+client.getReplyString());
-
String emailSuffix="163.com";
-
String emailPrefix="ranguisheng";
-
String fromEmail = emailPrefix+"@"+emailSuffix;
-
//Login to the SMTP server by sending the HELO command with the given hostname as an argument.
-
//Before performing any mail commands, you must first login.
-
//尝试和SMTP服务器建立连接,发送一条消息给SMTP服务器
-
client.login(emailPrefix);
-
logger.info("SMTPClient login:"+emailPrefix+"...");
-
logger.info("SMTPClient ReplyString:"+client.getReplyString());
-
-
//Set the sender of a message using the SMTP MAIL command,
-
//specifying a reverse relay path.
-
//The sender must be set first before any recipients may be specified,
-
//otherwise the mail server will reject your commands.
-
//设置发送者,在设置接受者之前必须要先设置发送者
-
client.setSender(fromEmail);
-
logger.info("设置发送者 :"+fromEmail);
-
logger.info("SMTPClient ReplyString:"+client.getReplyString());
-
//Add a recipient for a message using the SMTP RCPT command,
-
//specifying a forward relay path. The sender must be set first before any recipients may be specified,
-
//otherwise the mail server will reject your commands.
-
//设置接收者,在设置接受者必须先设置发送者,否则SMTP服务器会拒绝你的命令
-
client.addRecipient(email);
-
logger.info("设置接收者:"+email);
-
logger.info("SMTPClient ReplyString:"+client.getReplyString());
-
logger.info("SMTPClient ReplyCode:"+client.getReplyCode()+"(250表示正常)");
-
if (250 == client.getReplyCode()) {
-
return true;
-
}
-
} catch (Exception e) {
-
e.printStackTrace();
-
} finally {
-
try {
-
client.disconnect();
-
} catch (IOException e) {
-
}
-
}
-
return false;
-
}
-
public static void main(String[] args) {
-
System.out.println(isEmailValid("903109360@qq.com"));
-
}
-
}
执行结果:
MX record about qq.com exists.
Connection succeeded to mx3.qq.com.
220 newmx21.qq.com MX QQ Mail Server
>HELO 163.com
=250 newmx21.qq.com
>MAIL FROM: <163.com@ranguisheng>
=250 Ok
>RCPT TO: <903109360@qq.com>
=250 Ok
Outcome: true
如果将被验证的邮箱换为:903109360@qq.con,就会验证失败:
找不到MX记录
Outcome: false
值得注意的是犹豫校验的第一步是从DNS服务器查询MX记录 所以必须联网 否则校验会失效 因为找不到MX记录会导致真实的有效地址也校验为无效 这点要特别注意。
此代码需要两个jar包:
1、Apache Commons Net
maven地址:http://mvnrepository.com/artifact/commons-net/commons-net/
2、dnsjava
maven地址:http://mvnrepository.com/artifact/dnsjava/dnsjava/
PS:目前还没发验证部分企业邮箱,后面想办法解决这个问题之后更新此文章。
相关资源下载>>>:
参考文档:
3.检验电子邮件地址是否真实
https://blog.csdn.net/CrazyGou/article/details/1649834
同样用到了dnsjava(下载地址:http://www.dnsjava.org/)来获取MX记录。考虑到效率问题可将获取的MX记录保存到xml或数据库,下次使用时先检索本地数据,若不存在再获取其MX记录。
CheckEmail.java
import java.io.*;
import java.net.*;
import org.xbill.DNS.*;
public class CheckEmail
{
public static boolean check(String mailTo)
{
if (!mailTo.matches("w+([-_.]w+)*@w+([-.]w+)*.w+([-.]w+)*")) { //判断格式
return false;
}
String hostName = mailTo.split("@")[1]; //获得如163.com
String host = null; // MX记录
try {
Lookup lookup = new Lookup(hostName, Type.MX);
lookup.run();
if (lookup.getResult() != Lookup.SUCCESSFUL) {
return false;
}
else {
Record[] result = lookup.getAnswers();
host = result[0].getAdditionalName().toString();
}
Socket socket = new Socket(host, 25);
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
in.readLine();
out.write("HELO Sender ");
out.flush();
in.readLine();
out.write("MAIL FROM:<xx@xxx.xx> ");
out.flush();
in.readLine();
out.write("RCPT TO:<" + mailTo + "> ");
out.flush();
String r = in.readLine();
out.write("QUIT ");
out.flush();
out.close();
in.close();
socket.close();
if (!r.startsWith("250")) {
return false;
}
else {
return true;
}
} catch (Exception e) {
return false;
}
}
}
考虑到效率&资源问题,不推荐这样验证电子邮件的真实性。
4.使用JavaMail发送邮件时如何判断收件人邮箱是否有效的解决方案
https://blog.csdn.net/weixin_42023666/article/details/81347095
昨天公司老总提了个需求,要求给用户提供的邮箱发送邮件后,希望能收到反馈,判断是否发送成功。公司的项目是使用JavaMail来给用户发送邮件,但是调用Transport类的sendMessage方法发送邮件是没有返回值的。于是去百度,搜寻了好长时间,终于找到了两篇博客,以为能够解决这个问题,然后就去试了,可结果还是不行。
博客截图如下:
附上具体的地址:http://www.cnblogs.com/interdrp/p/5912723.html
具体的代码参考我找到的另一篇博客:https://blog.csdn.net/runyon1982/article/details/49018873
但是,不管我使用的是否是注册的还是未注册的邮箱,返回的结果都是: MESSAGE_DELIVERED,看了一下评论区的人,发现很多人情况和我都一看,心中充满了郁闷,因为找博客,找完整代码,整合到自己项目中来测试花了很多时间了,但是问题依旧没有解决。不过干咱们这行的,发牢骚、逃避是解决不了问题的,该干的活还是得干,于是调整了心情,我继续想办法。
我分析到:既然是要检验这个邮箱是否是有效的,那么给这个邮箱发一封邮件判断对方是否收到是一种解决方案,但却不是唯一的解决方案,我就想是不是有什么方法可以校验这个邮箱是否真实存在的呢?带着这个疑惑,我更换了在百度上用于搜索的关键字,果然,让我找到了一篇名为《Java与邮件系统交互之使用Socket验证邮箱是否存在》的博文,具体地址是:https://www.cnblogs.com/Johness/p/javaemail_usesockettocheckemailaddressvalid.html
我直接将这篇博客上的代码拷贝到我的项目上,测试了几个有效的邮箱,包括163邮箱,qq邮箱,腾讯企业邮箱,发现打印的结果都是true,然后又测试了几个不存在的邮箱,返回的结果都是false。此刻内心对这个作者充满了感激与膜拜......
为了表达对这段代码的喜爱,我特地复制上来跟各位一起欣赏:
package sy.util.sendemail;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.MXRecord;
import org.xbill.DNS.Record;
import org.xbill.DNS.TextParseException;
import org.xbill.DNS.Type;
public class MailValid {
public static void main(String[] args) {
// System.out.println(new MailValid().valid("a111aaaaaaaaaaa@163.com", "jootmir.org"));
}
/**
* 验证邮箱是否存在
* <br>
* 由于要读取IO,会造成线程阻塞
*
* @param toMail
* 要验证的邮箱
* @param domain
* 发出验证请求的域名(是当前站点的域名,可以任意指定)
* @return
* 邮箱是否可达
*/
public static boolean valid(String toMail, String domain) {
if(StringUtils.isBlank(toMail) || StringUtils.isBlank(domain)) return false;
if(!StringUtils.contains(toMail, '@')) return false;
String host = toMail.substring(toMail.indexOf('@') + 1);
if(host.equals(domain)) return false;
Socket socket = new Socket();
try {
// 查找mx记录
Record[] mxRecords = new Lookup(host, Type.MX).run();
if(ArrayUtils.isEmpty(mxRecords)) return false;
// 邮件服务器地址
String mxHost = ((MXRecord)mxRecords[0]).getTarget().toString();
if(mxRecords.length > 1) { // 优先级排序
List<Record> arrRecords = new ArrayList<Record>();
Collections.addAll(arrRecords, mxRecords);
Collections.sort(arrRecords, new Comparator<Record>() {
public int compare(Record o1, Record o2) {
return new CompareToBuilder().append(((MXRecord)o1).getPriority(), ((MXRecord)o2).getPriority()).toComparison();
}
});
mxHost = ((MXRecord)arrRecords.get(0)).getTarget().toString();
}
// 开始smtp
socket.connect(new InetSocketAddress(mxHost, 25));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(socket.getInputStream())));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
// 超时时间(毫秒)
long timeout = 6000;
// 睡眠时间片段(50毫秒)
int sleepSect = 50;
// 连接(服务器是否就绪)
if(getResponseCode(timeout, sleepSect, bufferedReader) != 220) {
return false;
}
// 握手
bufferedWriter.write("HELO " + domain + "\r\n");
bufferedWriter.flush();
if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
return false;
}
// 身份
bufferedWriter.write("MAIL FROM: <check@" + domain + ">\r\n");
bufferedWriter.flush();
if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
return false;
}
// 验证
bufferedWriter.write("RCPT TO: <" + toMail + ">\r\n");
bufferedWriter.flush();
if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
return false;
}
// 断开
bufferedWriter.write("QUIT\r\n");
bufferedWriter.flush();
return true;
} catch (NumberFormatException e) {
} catch (TextParseException e) {
} catch (IOException e) {
} catch (InterruptedException e) {
} finally {
try {
socket.close();
} catch (IOException e) {
}
}
return false;
}
private static int getResponseCode(long timeout, int sleepSect, BufferedReader bufferedReader) throws InterruptedException, NumberFormatException, IOException {
int code = 0;
for(long i = sleepSect; i < timeout; i += sleepSect) {
Thread.sleep(sleepSect);
if(bufferedReader.ready()) {
String outline = bufferedReader.readLine();
// FIXME 读完……
while(bufferedReader.ready())
/*System.out.println(*/bufferedReader.readLine()/*)*/;
/*System.out.println(outline);*/
code = Integer.parseInt(outline.substring(0, 3));
break;
}
}
return code;
}
}
原博客上的jar包下载链接找不到,附上在Maven Repository中的下载地址:
http://mvnrepository.com/artifact/dnsjava/dnsjava/2.1.1
要是该链接失效了,可以自己到Maven 仓库官网:http://mvnrepository.com/artifact/opensymphony/quartz-all ,输入关键字:dnsjava 搜索,注意选择 dnsjava包下的资源下载,如图:
对找jar包有过非常痛苦的经历,所以稍微啰嗦了一下,哈哈。言归正传,
接着,测试了邮箱有效之后,就可以给这个邮箱发邮件了,如果正常发送,说明发送成功了,因为邮箱是有效的,至于是不是用户本人的,那就只有用户自己知道了,如果发送过程中抛了异常,那就说明发送失败。
但是,这其实并非真正的解决方案,真正的解决方案是:
(1)如果这个邮箱对用户和对网站自身来说很重要的话,那么在注册的时候就应该强制用户验证邮箱,这样以后发邮箱时就能保证该邮箱是有效的。
(2)对于企业邮箱,有可能用户会因为离职等原因,导致原来的邮箱不可用,或者对于那些用户主动关闭邮箱的情况,就需要调用工具类定时检验邮箱是否可用的。但是我们知道,IO是非常耗费计算机资源的,所以有必要降低IO的频率,同时避免在网站使用的高峰期进行大量的IO操作。
至此,问题勉强解决了。如果有更好的方法,欢迎指教。
解决方法一直都在,只是没有人将javamail与Socket校验这两个词关联起来,我今天做的,就是关联这两个词,仅此而已。
5.检验Email是否有效
https://blog.csdn.net/weiren2006/article/details/4053161
前段时间自己做一个检验Email是否有效的工具,通过几天时间的查资料和学习,终于搞定了。主要就是登陆邮箱的smtp服务器,查找邮箱是否存在,在网上很多转载了检验Email有效性的文章,那里就是通过smtp检验的,首先说一下使用telnet登陆smtp服务器发送邮件的过程:
1、连接smtp服务器:telnet smtp.126.com 25
2、发送hello命令: helo 126.com
3、发送mail from命令,表示发件人邮箱:mail from:<test@test.com>
4、发送rcpt to命令,表示收件人邮箱,可以多次使用此命令:rcpt to:<test@126.com>
5、发送data命令,接着就是信件的内容,以“.”结束
6、发送quit命令,结束连接。
上面的每个命令都以/r/n结束,这是smtp协议。但是如今大部分邮件服务器都采用esmtp协议,防止乱发邮件,esmtp也就比smtp多了验证的步骤,在第2步与第3步之间加入验证:
发送auth login命令:auth login
接着发送用户名和密码,用户名和密码都是base64编码的,确认了用户名和密码后就可以发送mail from了。
这样我们就可以通过rcpt to返回的信息判断用户是否存在了。
后来我查了一下,有些在线检测邮箱有效性的网站,它们的log中显示的并不用用户名和密码登陆,也就是可以直接利用smtp协议,然后查找到它们是利用MX服务器检测的。
我们可以利用nslookup命令查找到邮箱服务器的MX服务器地址,如nslookup -type=mx 126.com
这样就会显示出126邮箱的MX服务器“126.com MX preference = 50, mail exchanger = 126.mxmail.netease.com”其中126.mxmail.netease.com就是地址了。下面是交互的过程
$ telnet 126.mxmail.netease.com 25
Trying 220.181.15.200...
Connected to 126.mxmail.netease.com.
S:220 126.com Anti-spam GT for Coremail System (126com[20081010])
C:helo localhost
S:250 OK
C:mail from:<info@localhost.org> //这个可以随便写,只要不是126的就行
S:250 Mail OK
C:rcpt to:<test@126.com>
S:250 Mail OK //250,表示此邮箱存在
C:rcpt to:<test_1234568@126.com>
S:550 User not found: test_1234568@126.com //550,用户不存在
C:quit
S:221 Bye
Connection closed by foreign host.
这样如果我们要检测某个邮箱是否有效,我们只要登陆此邮箱的MX服务器,然后检测就OK了。
如果要编程实现的话,只需建立socket连接,然后发送相应的命令,检查接受到的是不是250信息就可以判断邮箱的有效性,这里要注意每次命令都要加上/r/n结束,另外还有一个重点就是要获取MX地址,这个就根据各个开发语言和工具,仁者见仁智者见智,不同的工具实现也不一样的。我用VC和C#实现过,就是运行控制台命令,然后获得命令的标准输出。
6.
如何验证一个邮箱是否正确
https://waimaoquan.alibaba.com/bbs/read-htm-tid-2006059-fid-0.html
有时我们给一些潜在客户发了第一封邮件,邮件很快被退回来了,也就是说发送失败了,邮件发送失败的原因有很多,最可能的一种就是邮箱地址有误,根本不存在。
如果我们发邮件失败后,我们可以去判断那个邮箱地址是不是存在的,如果不存在,我们可以把这个邮箱从我们的邮箱库中删除,这样就能提高以后的工作效率。另外,如果我们发的很多邮件被退回来,事实上我们的邮箱是会被减分的,那我们邮箱的等级也会相应降低一些,严重的可能会影响我们跟正常合作的客户的邮件沟通。
判断一个邮箱是否存在的工具很多,这里介绍几种我经常用的,效果比较好的。
第一个工具:
http://verify-email.org/
进入这个网站,有个地方有个方框,我们把要验证的邮箱地址粘贴进去,然后点击“Verify",过一会儿,下面会显示出结果,如果是Result: Ok,说明邮箱是存在的,如果显示是Result: bad,那邮箱地址一般存在问题
这个工具每小时好像最多只能验证5个邮箱
第二个工具:
http://mailtester.com/
使用方法很简单,这里不说了。请注意,如果系统说“Server doesn't allow e-mail address verification”,这种情况,就是无法判断的意思,这种情况邮箱一般也是没有问题的。
第三个工具:
http://tools.email-checker.com/ ,这个网站一般验证不出企业邮箱。
请注意,我们去验证的时候,最好不要拿企业邮箱去验证,企业邮箱的后缀一个公司的域名后缀,如果你把这个企业邮箱放在谷歌里搜一下,能搜到一个公司的网站,那一般邮箱就没问题。另外,一般人也不会去虚构企业邮箱,因为没什么意义。
以上的验证工具我们主要是用它们来验证gmail,yahoo email,hotmail之类免费邮箱,另外,那些只是工具,准确率一般就95%左右,不是100%准确,就是一些付费的邮箱验证工具,准确率也不太可能达到100%。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)