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

版权声明:襄阳雷哥的版权声明 https://blog.csdn.net/FansUnion/article/details/88714293

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

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

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

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

简单做个汇总,备忘。

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

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

微软Hotmail邮箱,返回超时。

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

[email protected] 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("[email protected]", "jootmir.org"));
        System.out.println("应该为true:"+new MailValidKit().valid("[email protected]", "jootmir.org"));
        System.out.println("应该为false:"+new MailValidKit().valid("[email protected]", "jootmir.org"));
        System.out.println("应该为true:"+new MailValidKit().valid("[email protected]", "jootmir.org"));
        System.out.println("应该为true:"+new MailValidKit().valid("[email protected]", "jootmir.org"));
        System.out.println("应该为true:"+new MailValidKit().valid("[email protected]", "jootmir.org"));
        System.out.println("应该为true:"+new MailValidKit().valid("[email protected]", "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("[email protected]"));
		System.out.println("合法hotmail邮箱:"+isEmailValid("[email protected]"));
	}

	//550
	//设置接收者:[email protected]
	//SMTPClient ReplyString:550 5.1.1 Recipient address rejected: User unknown
	private static void testJd() {
		System.out.println("jd企业邮箱:"+isEmailValid("[email protected]"));
	}

	private static void test163() {
		//163总是false,550、503等异常状态,而不是250
		
		System.out.println("合法163邮箱:"+isEmailValid("[email protected]"));
	}

	private static void testQq() {
		//非法格式
		System.out.println("格式不正确:"+isEmailValid("90310930qq.com"));
		System.out.println("格式不正确2:"+isEmailValid("903109360@qqcom"));
		//普通随机邮箱
		System.out.println("普通随机邮箱:"+isEmailValid("[email protected]"));//原作者自己测试等邮箱
		System.out.println("普通随机邮箱,瞎填的:"+isEmailValid("[email protected]"));
		//我的邮箱,qq
		System.out.println("合法qq数字邮箱:"+isEmailValid("[email protected]"));
		System.out.println("合法qq自定义邮箱:"+isEmailValid("[email protected]"));
	}

	private static void testBaing() {
		//baron企业邮箱ok
		System.out.println("企业邮箱:"+isEmailValid("[email protected]"));
		System.out.println("企业邮箱2:"+isEmailValid("[email protected]"));
		System.out.println("企业邮箱3:"+isEmailValid("[email protected]"));
		System.out.println("企业邮箱4:"+isEmailValid("[email protected]"));
		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("[email protected]"));//check方式有问题
		System.out.println("qq邮箱:"+CheckEmail.check("[email protected]"));//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:<[email protected]> ");
            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邮箱[email protected]。我们填写地址是到“qq.com”,但实际上可能服务器地址千奇百怪/而且许有多个。在设置DNS时可以顺带设置MX记录。

  说白了,“qq.com”只是域名,做HTTP请求响应地址,你邮件能发到Tomcat上吗?那我们发到“qq.com”上面的邮件哪里去了,我们把自己想象成一个邮件服务器,你的用户让你给[email protected]发一封信,你如何操作?找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("[email protected]", "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("[email protected]"));

  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: <[email protected]>
=250 Ok

Outcome: true

如果将被验证的邮箱换为:[email protected],就会验证失败:

找不到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:<[email protected]> ");
            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("[email protected]", "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:<[email protected]>

4、发送rcpt to命令,表示收件人邮箱,可以多次使用此命令:rcpt to:<[email protected]>

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:<[email protected]>           //这个可以随便写,只要不是126的就行
S:250 Mail OK

C:rcpt to:<[email protected]>
S:250 Mail OK                                         //250,表示此邮箱存在
C:rcpt to:<[email protected]>
S:550 User not found: [email protected]              //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%。

猜你喜欢

转载自blog.csdn.net/FansUnion/article/details/88714293
今日推荐