当前位置:   article > 正文

检验电子邮件地址是否真实存在_验证邮箱是否存在

验证邮箱是否存在

新项目,有需要提前“判断电子邮件地址是否真实存在”。

首先想到这是一个标准化问题,网上肯定有参考答案了。

大概思路是,发一封邮件给这个账户,或者通过SMTP等协议进行通信。

邮箱几十亿,不可能有简单的API,直接判断是否有效,不然全网等可用邮箱都被你给拿到了。

 

简单做个汇总,备忘。

实践结论:QQ、163等标准化邮箱,可以判断是否存在。部分企业邮箱,如果使用的是腾讯企业邮箱,也可以。

部分企业邮箱,京东等,不太行。

微软Hotmail邮箱,返回超时。

 

在线检测工具,比如 https://verify-email.org/ 感觉不靠谱。

2037088@qq.com is BAD

从了解到放弃。

实际测试的邮箱,已做修改,仅供参考。

 

第1种实践过的,最佳代码:

  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. import org.apache.commons.lang.ArrayUtils;
  14. import org.apache.commons.lang.StringUtils;
  15. import org.apache.commons.lang.builder.CompareToBuilder;
  16. import org.xbill.DNS.Lookup;
  17. import org.xbill.DNS.MXRecord;
  18. import org.xbill.DNS.Record;
  19. import org.xbill.DNS.TextParseException;
  20. import org.xbill.DNS.Type;
  21. public class MailValidKit {
  22. public static void main(String[] args) {
  23. System.out.println("应该为true:"+new MailValidKit().valid("100582783@qq.com", "jootmir.org"));
  24. System.out.println("应该为true:"+new MailValidKit().valid("fasuion@qq.com", "jootmir.org"));
  25. System.out.println("应该为false:"+new MailValidKit().valid("fansnion@qq.com", "jootmir.org"));
  26. System.out.println("应该为true:"+new MailValidKit().valid("lewenans@163.com", "jootmir.org"));
  27. System.out.println("应该为true:"+new MailValidKit().valid("wen.lei@10cdit.com", "jootmir.org"));
  28. System.out.println("应该为true:"+new MailValidKit().valid("ein1@jd.com", "jootmir.org"));
  29. System.out.println("应该为true:"+new MailValidKit().valid("wngongao@jd.com", "jootmir.org"));
  30. }
  31. /**
  32. * 验证邮箱是否存在
  33. * <br>
  34. * 由于要读取IO,会造成线程阻塞
  35. *
  36. * @param toMail
  37. * 要验证的邮箱
  38. * @param domain
  39. * 发出验证请求的域名(是当前站点的域名,可以任意指定)
  40. * @return
  41. * 邮箱是否可达
  42. */
  43. public boolean valid(String toMail, String domain) {
  44. if(StringUtils.isBlank(toMail) || StringUtils.isBlank(domain)) return false;
  45. if(!StringUtils.contains(toMail, '@')) return false;
  46. String host = toMail.substring(toMail.indexOf('@') + 1);
  47. if(host.equals(domain)) return false;
  48. Socket socket = new Socket();
  49. try {
  50. // 查找mx记录
  51. Record[] mxRecords = new Lookup(host, Type.MX).run();
  52. if(ArrayUtils.isEmpty(mxRecords)) return false;
  53. // 邮件服务器地址
  54. String mxHost = ((MXRecord)mxRecords[0]).getTarget().toString();
  55. if(mxRecords.length > 1) { // 优先级排序
  56. List<Record> arrRecords = new ArrayList<Record>();
  57. Collections.addAll(arrRecords, mxRecords);
  58. Collections.sort(arrRecords, new Comparator<Record>() {
  59. public int compare(Record o1, Record o2) {
  60. return new CompareToBuilder().append(((MXRecord)o1).getPriority(), ((MXRecord)o2).getPriority()).toComparison();
  61. }
  62. });
  63. mxHost = ((MXRecord)arrRecords.get(0)).getTarget().toString();
  64. }
  65. // 开始smtp
  66. socket.connect(new InetSocketAddress(mxHost, 25));
  67. BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(socket.getInputStream())));
  68. BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
  69. // 超时时间(毫秒)
  70. long timeout = 6000;
  71. // 睡眠时间片段(50毫秒)
  72. int sleepSect = 50;
  73. // 连接(服务器是否就绪)
  74. if(getResponseCode(timeout, sleepSect, bufferedReader) != 220) {
  75. return false;
  76. }
  77. // 握手
  78. bufferedWriter.write("HELO " + domain + "\r\n");
  79. bufferedWriter.flush();
  80. if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
  81. return false;
  82. }
  83. // 身份
  84. bufferedWriter.write("MAIL FROM: <check@" + domain + ">\r\n");
  85. bufferedWriter.flush();
  86. if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
  87. return false;
  88. }
  89. // 验证
  90. bufferedWriter.write("RCPT TO: <" + toMail + ">\r\n");
  91. bufferedWriter.flush();
  92. if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
  93. return false;
  94. }
  95. // 断开
  96. bufferedWriter.write("QUIT\r\n");
  97. bufferedWriter.flush();
  98. return true;
  99. } catch (NumberFormatException e) {
  100. } catch (TextParseException e) {
  101. } catch (IOException e) {
  102. } catch (InterruptedException e) {
  103. } finally {
  104. try {
  105. socket.close();
  106. } catch (IOException e) {
  107. }
  108. }
  109. return false;
  110. }
  111. private int getResponseCode(long timeout, int sleepSect, BufferedReader bufferedReader) throws InterruptedException, NumberFormatException, IOException {
  112. int code = 0;
  113. for(long i = sleepSect; i < timeout; i += sleepSect) {
  114. Thread.sleep(sleepSect);
  115. if(bufferedReader.ready()) {
  116. String outline = bufferedReader.readLine();
  117. // FIXME 读完……
  118. while(bufferedReader.ready())
  119. /*System.out.println(*/bufferedReader.readLine()/*)*/;
  120. /*System.out.println(outline);*/
  121. code = Integer.parseInt(outline.substring(0, 3));
  122. break;
  123. }
  124. }
  125. return code;
  126. }
  127. }

代码主要参考:https://www.cnblogs.com/Johness/p/javaemail_usesockettocheckemailaddressvalid.html

 

第2种,还可以,163邮箱无法识别

  1. import java.io.IOException;
  2. import org.apache.commons.net.smtp.SMTPClient;
  3. import org.apache.commons.net.smtp.SMTPReply;
  4. import org.apache.log4j.Logger;
  5. import org.xbill.DNS.Lookup;
  6. import org.xbill.DNS.Record;
  7. import org.xbill.DNS.Type;
  8. /**
  9. *
  10. * 校验邮箱:1、格式是否正确 2、是否真实有效的邮箱地址
  11. * 步骤: 1、从dns缓存服务器上查询邮箱域名对应的SMTP服务器地址 2、尝试建立Socket连接
  12. * 3、尝试发送一条消息给SMTP服务器 4、设置邮件发送者 5、设置邮件接收者 6、检查响应码是否为250(为250则说明这个邮箱地址是真实有效的)
  13. *
  14. * @author Michael Ran
  15. *
  16. */
  17. // 总体靠谱
  18. public class CheckEmailValidityUtil {
  19. private static final Logger logger = Logger.getLogger(CheckEmailValidityUtil.class);
  20. /**
  21. * @param email
  22. * 待校验的邮箱地址
  23. * @return
  24. */
  25. public static boolean isEmailValid(String email) {
  26. if (!email.matches("[\\w\\.\\-]+@([\\w\\-]+\\.)+[\\w\\-]+")) {
  27. logger.error("邮箱(" + email + ")校验未通过,格式不对!");
  28. return false;
  29. }
  30. String host = "";
  31. String hostName = email.split("@")[1];
  32. // Record: A generic DNS resource record. The specific record types
  33. // extend this class. A record contains a name, type, class, ttl, and
  34. // rdata.
  35. Record[] result = null;
  36. SMTPClient client = new SMTPClient();
  37. try {
  38. // 查找DNS缓存服务器上为MX类型的缓存域名信息
  39. Lookup lookup = new Lookup(hostName, Type.MX);
  40. lookup.run();
  41. if (lookup.getResult() != Lookup.SUCCESSFUL) {// 查找失败
  42. logger.error("邮箱(" + email + ")校验未通过,未找到对应的MX记录!");
  43. return false;
  44. } else {// 查找成功
  45. result = lookup.getAnswers();
  46. }
  47. // 尝试和SMTP邮箱服务器建立Socket连接
  48. for (int i = 0; i < result.length; i++) {
  49. host = result[i].getAdditionalName().toString();
  50. logger.info("SMTPClient try connect to host:" + host);
  51. // 此connect()方法来自SMTPClient的父类:org.apache.commons.net.SocketClient
  52. // 继承关系结构:org.apache.commons.net.smtp.SMTPClient-->org.apache.commons.net.smtp.SMTP-->org.apache.commons.net.SocketClient
  53. // Opens a Socket connected to a remote host at the current
  54. // default port and
  55. // originating from the current host at a system assigned port.
  56. // Before returning,
  57. // _connectAction_() is called to perform connection
  58. // initialization actions.
  59. // 尝试Socket连接到SMTP服务器
  60. client.connect(host);
  61. // Determine if a reply code is a positive completion
  62. // response(查看响应码是否正常).
  63. // All codes beginning with a 2 are positive completion
  64. // responses(所有以2开头的响应码都是正常的响应).
  65. // The SMTP server will send a positive completion response on
  66. // the final successful completion of a command.
  67. if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) {
  68. // 断开socket连接
  69. client.disconnect();
  70. continue;
  71. } else {
  72. logger.info("找到MX记录:" + hostName);
  73. logger.info("建立链接成功:" + hostName);
  74. break;
  75. }
  76. }
  77. logger.info("SMTPClient ReplyString:" + client.getReplyString());
  78. //网易163,发现是自己等,有能力进行检测,所以最好用真实的
  79. String emailPrefix = "leiwenfans";//leiwenfans222,可以随便写?
  80. String emailSuffix = "163.com";
  81. //emailPrefix="fansunion";
  82. //emailSuffix="qq.com";
  83. String fromEmail = emailPrefix + "@" + emailSuffix;
  84. // Login to the SMTP server by sending the HELO command with the
  85. // given hostname as an argument.
  86. // Before performing any mail commands, you must first login.
  87. // 尝试和SMTP服务器建立连接,发送一条消息给SMTP服务器
  88. client.login(emailPrefix);
  89. logger.info("SMTPClient login:" + emailPrefix + "...");
  90. logger.info("SMTPClient ReplyString:" + client.getReplyString());
  91. // Set the sender of a message using the SMTP MAIL command,
  92. // specifying a reverse relay path.
  93. // The sender must be set first before any recipients may be
  94. // specified,
  95. // otherwise the mail server will reject your commands.
  96. // 设置发送者,在设置接受者之前必须要先设置发送者
  97. client.setSender(fromEmail);
  98. logger.info("设置发送者 :" + fromEmail);
  99. logger.info("SMTPClient ReplyString:" + client.getReplyString());
  100. // Add a recipient for a message using the SMTP RCPT command,
  101. // specifying a forward relay path. The sender must be set first
  102. // before any recipients may be specified,
  103. // otherwise the mail server will reject your commands.
  104. // 设置接收者,在设置接受者必须先设置发送者,否则SMTP服务器会拒绝你的命令
  105. client.addRecipient(email);
  106. logger.info("设置接收者:" + email);
  107. logger.info("SMTPClient ReplyString:" + client.getReplyString());
  108. logger.info("SMTPClient ReplyCode:" + client.getReplyCode() + "(250表示正常)");
  109. if (250 == client.getReplyCode()) {
  110. return true;
  111. }
  112. } catch (Exception e) {
  113. e.printStackTrace();
  114. } finally {
  115. try {
  116. client.disconnect();
  117. } catch (IOException e) {
  118. }
  119. }
  120. return false;
  121. }
  122. //建立连接可能会非常慢,几十秒
  123. public static void main(String[] args) {
  124. //test163();
  125. testJd();
  126. //testBrng();
  127. //testQq();
  128. testHotmail();
  129. }
  130. //SMTPClient login:leiwenfans...
  131. //SMTPClient ReplyString:250 DB5EUR03FT043.mail.protection.outlook.com Hello [36.110.19.90]
  132. //没下文了
  133. private static void testHotmail() {
  134. System.out.println("合法hotmail邮箱:"+isEmailValid("fasnion@otmail.com"));
  135. System.out.println("合法hotmail邮箱:"+isEmailValid("huingans@homail.com"));
  136. }
  137. //550
  138. //设置接收者:liwe@jd.com
  139. //SMTPClient ReplyString:550 5.1.1 Recipient address rejected: User unknown
  140. private static void testJd() {
  141. System.out.println("jd企业邮箱:"+isEmailValid("eien1@jd.com"));
  142. }
  143. private static void test163() {
  144. //163总是false550503等异常状态,而不是250
  145. System.out.println("合法163邮箱:"+isEmailValid("liwens@163.com"));
  146. }
  147. private static void testQq() {
  148. //非法格式
  149. System.out.println("格式不正确:"+isEmailValid("90310930qq.com"));
  150. System.out.println("格式不正确2:"+isEmailValid("903109360@qqcom"));
  151. //普通随机邮箱
  152. System.out.println("普通随机邮箱:"+isEmailValid("903109360@qq.com"));//原作者自己测试等邮箱
  153. System.out.println("普通随机邮箱,瞎填的:"+isEmailValid("903109360222@qq.com"));
  154. //我的邮箱,qq
  155. System.out.println("合法qq数字邮箱:"+isEmailValid("2437088@qq.com"));
  156. System.out.println("合法qq自定义邮箱:"+isEmailValid("fasunon@qq.com"));
  157. }
  158. private static void testBaing() {
  159. //baron企业邮箱ok
  160. System.out.println("企业邮箱:"+isEmailValid("wn.li@10edit.com"));
  161. System.out.println("企业邮箱2:"+isEmailValid("cheong.yu@1dit.com"));
  162. System.out.println("企业邮箱3:"+isEmailValid("yonao.niu@10edit.com"));
  163. System.out.println("企业邮箱4:"+isEmailValid("yonghao.@.com"));
  164. System.out.println("企业邮箱:"+isEmailValid("wen.i123@100crdi"));
  165. }
  166. }

第3种,不太靠谱,io卡住,源代码中得正则表达式还有问题

  1. import java.io.*;
  2. import java.net.*;
  3. import org.xbill.DNS.*;
  4. //https://blog.csdn.net/CrazyGou/article/details/1649834 不太靠谱
  5. public class CheckEmail
  6. {
  7. public static void main(String[] args) {
  8. System.out.println("企业邮箱:"+CheckEmail.check("wen.lei@100ct.com"));//check方式有问题
  9. System.out.println("qq邮箱:"+CheckEmail.check("2403818@qq.com"));//check方式有问题
  10. }
  11. public static boolean check(String mailTo)
  12. {
  13. //正则不对
  14. /* if (!mailTo.matches("w+([-_.]w+)*@w+([-.]w+)*.w+([-.]w+)*")) { //判断格式
  15. return false;
  16. }*/
  17. if (!mailTo.matches("[\\w\\.\\-]+@([\\w\\-]+\\.)+[\\w\\-]+")) {
  18. System.out.println("邮箱(" + mailTo + ")校验未通过,格式不对!");
  19. return false;
  20. }
  21. String hostName = mailTo.split("@")[1]; //获得如163.com
  22. String host = null; // MX记录
  23. try {
  24. Lookup lookup = new Lookup(hostName, Type.MX);
  25. lookup.run();
  26. if (lookup.getResult() != Lookup.SUCCESSFUL) {
  27. return false;
  28. }
  29. else {
  30. Record[] result = lookup.getAnswers();
  31. host = result[0].getAdditionalName().toString();
  32. }
  33. Socket socket = new Socket(host, 25);
  34. BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
  35. BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  36. in.readLine();
  37. out.write("HELO Sender ");
  38. out.flush();
  39. in.readLine();
  40. out.write("MAIL FROM:<xx@xxx.xx> ");
  41. out.flush();
  42. in.readLine();
  43. out.write("RCPT TO:<" + mailTo + "> ");
  44. out.flush();
  45. String r = in.readLine();
  46. out.write("QUIT ");
  47. out.flush();
  48. out.close();
  49. in.close();
  50. socket.close();
  51. if (!r.startsWith("250")) {
  52. return false;
  53. }
  54. else {
  55. return true;
  56. }
  57. } catch (Exception e) {
  58. return false;
  59. }
  60. }
  61. }

----------------------以下内容纯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次。

代码如下(补充了一些注释):

 

 
  1. import java.io.IOException;

  2.  
  3. import org.apache.commons.net.smtp.SMTPClient;

  4. import org.apache.commons.net.smtp.SMTPReply;

  5. import org.apache.log4j.Logger;

  6. import org.xbill.DNS.Lookup;

  7. import org.xbill.DNS.Record;

  8. import org.xbill.DNS.Type;

  9.  
  10. /**

  11.  *

  12.  * 校验邮箱:1、格式是否正确 2、是否真实有效的邮箱地址

  13.  * 步骤:

  14.  * 1、从dns缓存服务器上查询邮箱域名对应的SMTP服务器地址

  15.  * 2、尝试建立Socket连接

  16.  * 3、尝试发送一条消息给SMTP服务器

  17.  * 4、设置邮件发送者

  18.  * 5、设置邮件接收者

  19.  * 6、检查响应码是否为250(为250则说明这个邮箱地址是真实有效的)

  20.  * @author Michael Ran

  21.  *

  22.  */

  23. public class CheckEmailValidityUtil {

  24.     private static final Logger logger = Logger

  25.             .getLogger(CheckEmailValidityUtil.class);

  26.     /**

  27.      * @param email 待校验的邮箱地址

  28.      * @return

  29.      */

  30.     public static boolean isEmailValid(String email) {

  31.         if (!email.matches("[\\w\\.\\-]+@([\\w\\-]+\\.)+[\\w\\-]+")) {

  32.             logger.error("邮箱("+email+")校验未通过,格式不对!");

  33.             return false;

  34.         }

  35.         String host = "";

  36.         String hostName = email.split("@")[1];

  37.         //Record: A generic DNS resource record. The specific record types

  38.         //extend this class. A record contains a name, type, class, ttl, and rdata.

  39.         Record[] result = null;

  40.         SMTPClient client = new SMTPClient();

  41.         try {

  42.             // 查找DNS缓存服务器上为MX类型的缓存域名信息

  43.             Lookup lookup = new Lookup(hostName, Type.MX);

  44.             lookup.run();

  45.             if (lookup.getResult() != Lookup.SUCCESSFUL) {//查找失败

  46.                 logger.error("邮箱("+email+")校验未通过,未找到对应的MX记录!");

  47.                 return false;

  48.             } else {//查找成功

  49.                 result = lookup.getAnswers();

  50.             }

  51.             //尝试和SMTP邮箱服务器建立Socket连接

  52.             for (int i = 0; i < result.length; i++) {

  53.                 host = result[i].getAdditionalName().toString();

  54.                 logger.info("SMTPClient try connect to host:"+host);

  55.                 

  56.                 //此connect()方法来自SMTPClient的父类:org.apache.commons.net.SocketClient

  57.                 //继承关系结构:org.apache.commons.net.smtp.SMTPClient-->org.apache.commons.net.smtp.SMTP-->org.apache.commons.net.SocketClient

  58.                 //Opens a Socket connected to a remote host at the current default port and

  59.                 //originating from the current host at a system assigned port. Before returning,

  60.                 //_connectAction_() is called to perform connection initialization actions.

  61.                 //尝试Socket连接到SMTP服务器

  62.                 client.connect(host);

  63.                 //Determine if a reply code is a positive completion response(查看响应码是否正常).

  64.                 //All codes beginning with a 2 are positive completion responses(所有以2开头的响应码都是正常的响应).

  65.                 //The SMTP server will send a positive completion response on the final successful completion of a command.

  66.                 if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) {

  67.                     //断开socket连接

  68.                     client.disconnect();

  69.                     continue;

  70.                 } else {

  71.                     logger.info("找到MX记录:"+hostName);

  72.                     logger.info("建立链接成功:"+hostName);

  73.                     break;

  74.                 }

  75.             }

  76.             logger.info("SMTPClient ReplyString:"+client.getReplyString());

  77.             String emailSuffix="163.com";

  78.             String emailPrefix="ranguisheng";

  79.             String fromEmail = emailPrefix+"@"+emailSuffix;

  80.             //Login to the SMTP server by sending the HELO command with the given hostname as an argument.

  81.             //Before performing any mail commands, you must first login.

  82.             //尝试和SMTP服务器建立连接,发送一条消息给SMTP服务器

  83.             client.login(emailPrefix);

  84.             logger.info("SMTPClient login:"+emailPrefix+"...");

  85.             logger.info("SMTPClient ReplyString:"+client.getReplyString());

  86.             

  87.             //Set the sender of a message using the SMTP MAIL command,

  88.             //specifying a reverse relay path.

  89.             //The sender must be set first before any recipients may be specified,

  90.             //otherwise the mail server will reject your commands.

  91.             //设置发送者,在设置接受者之前必须要先设置发送者

  92.             client.setSender(fromEmail);

  93.             logger.info("设置发送者 :"+fromEmail);

  94.             logger.info("SMTPClient ReplyString:"+client.getReplyString());

  95.  
  96.             //Add a recipient for a message using the SMTP RCPT command,

  97.             //specifying a forward relay path. The sender must be set first before any recipients may be specified,

  98.             //otherwise the mail server will reject your commands.

  99.             //设置接收者,在设置接受者必须先设置发送者,否则SMTP服务器会拒绝你的命令

  100.             client.addRecipient(email);

  101.             logger.info("设置接收者:"+email);

  102.             logger.info("SMTPClient ReplyString:"+client.getReplyString());

  103.             logger.info("SMTPClient ReplyCode:"+client.getReplyCode()+"(250表示正常)");

  104.             if (250 == client.getReplyCode()) {

  105.                 return true;

  106.             }

  107.         } catch (Exception e) {

  108.             e.printStackTrace();

  109.         } finally {

  110.             try {

  111.                 client.disconnect();

  112.             } catch (IOException e) {

  113.             }

  114.         }

  115.         return false;

  116.     }

  117.     public static void main(String[] args) {

  118.         System.out.println(isEmailValid("903109360@qq.com"));

  119.     }

  120. }


执行结果:

 

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:目前还没发验证部分企业邮箱,后面想办法解决这个问题之后更新此文章。

相关资源下载>>>:

dnsjava下载

Apache-commons-net下载 


参考文档:

apache-commons-net API

dnsjava API

 

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%。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/196186
推荐阅读
相关标签
  

闽ICP备14008679号