Phương thức xác thực địa chỉ email Java tốt nhất là gì? [đóng cửa]


247

Các thư viện xác thực địa chỉ email tốt cho Java là gì? Có bất kỳ lựa chọn thay thế cho trình xác nhận commons ?


15
Tôi sẽ chỉ để lại này ở đây: davidcelis.com/blog/2012/09/06/...
mpenkov


Bạn không nên sử dụng các thư viện (hoặc biểu thức chính quy) không xác thực toàn diện. Do sự phức tạp của địa chỉ email hợp lệ, không có trung gian giữa không có xác nhận hợp lệ và xác nhận toàn diện. Việc triển khai của Apache Commons không toàn diện. Tôi chỉ biết một thư viện duy nhất ( email-rfc2822-validator ), nhưng nó vẫn hoạt động với các biểu thức lớn. Một lexer toàn diện là những gì bạn thực sự muốn. EmailValidator4J nói rằng nó thực hiện công việc, nhưng tôi không có kinh nghiệm với nó.
Benny Bottema

1
@BennyBottema Thay vì chỉnh sửa câu hỏi bằng bình luận, vui lòng tạo một bài đăng Meta để thảo luận về lý do tại sao điều này bị đóng nếu bạn vẫn còn thắc mắc.
Machavity

Câu trả lời:


134

Apache Commons thường được biết đến như một dự án vững chắc. Tuy nhiên, hãy nhớ rằng, bạn vẫn sẽ phải gửi email xác minh đến địa chỉ nếu bạn muốn đảm bảo đó là email thực và chủ sở hữu muốn sử dụng nó trên trang web của bạn.

EDIT : Có một lỗi trong đó quá hạn chế về tên miền, khiến nó không chấp nhận các email hợp lệ từ các TLD mới.

Lỗi này đã được giải quyết vào ngày 03 tháng 1/15 02:48 trong phiên bản commons-validator phiên bản 1.4.1


1
Tôi đồng ý với các bit bổ sung mà bạn đã trích dẫn, nhưng đó có phải là một phần của dự án Xác thực Commons không?
duffymo

2
Không, EmailValidatorlớp Apache không gửi thông báo email để xác minh.
Matthew Flaschen

3
Nếu trường hợp sử dụng của bạn là để xác thực địa chỉ email từ xa của người dùng, thì giải pháp này có một lỗ hổng đáng kể (tương tự InternetAddress.validate ()): EmailValidator coi người dùng @ [10.9.8.7] là một địa chỉ email hợp lệ - mà họ đang theo RFC, nhưng có thể không cho đăng ký người dùng / mẫu liên hệ.
zillion1

1
@zillion, được ghi lại trong Apache COmmons: "Việc triển khai này không được đảm bảo để bắt tất cả các lỗi có thể có trong một địa chỉ email." Và tôi đã nói những gì bạn phải làm để "đảm bảo đó là một email thực sự". Tuy nhiên, địa chỉ với IP cục bộ có thể hợp lệ trong các môi trường hiếm.
Matthew Flaschen

5
Apache Commons EmailValidator có một nhược điểm nghiêm trọng: không hỗ trợ IDN.
Piohen

261

Sử dụng gói email java chính thức là dễ nhất:

public static boolean isValidEmailAddress(String email) {
   boolean result = true;
   try {
      InternetAddress emailAddr = new InternetAddress(email);
      emailAddr.validate();
   } catch (AddressException ex) {
      result = false;
   }
   return result;
}

59
Lưu ý rằng InternetAddress.validate () coi người dùng @ [10.9.8.7] và người dùng @ localhost là địa chỉ email hợp lệ - theo địa chỉ RFC. Mặc dù, tùy thuộc vào trường hợp sử dụng (mẫu web), bạn có thể muốn coi chúng là không hợp lệ.
zillion1

8
không chỉ có giá trị như @ zillion1 đã nói, mà cả những thứ như bla @ bla cũng được coi là hợp lệ. Thực sự không phải là giải pháp tốt nhất.
Diego Plentz

4
@NicholasTcarCottrell Đây là Java, ở đây chúng tôi ném và bắt ngoại lệ, tôi không thực sự hiểu ý của bạn
gyorgyabraham

17
Tôi nghi ngờ rằng nhà xây dựng InternetAddress đã bị giả mạo. Hoặc hệ thống của tôi đã bị giả mạo. Hoặc RFC822 đã bị giả mạo. Hoặc tôi thực sự có thể sử dụng một số giấc ngủ ngay bây giờ. Nhưng tôi vừa thử một số mã và năm chuỗi sau đây đều chuyển qua dưới dạng địa chỉ email hợp lệ nếu bạn chuyển chúng cho nhà xây dựng InternetAddress và "rõ ràng", chúng không hợp lệ. Here We Go: ., .com, com., abc123. Ngoài ra, việc thêm khoảng trắng ở đầu hoặc cuối cũng không làm mất hiệu lực của chuỗi. Bạn là thẩm phán!
Martin Andersson

4
um, phô mai thất bại đúng cách khi tôi chạy nó bạn đang liên kết đến thư viện javax.mail nào vậy ???
Aaron Davidson

91

Trình xác thực Apache Commons có thể được sử dụng như được đề cập trong các câu trả lời khác.

tệp pom.xml:

<dependency>
    <groupId>commons-validator</groupId>
    <artifactId>commons-validator</artifactId>
    <version>1.4.1</version>
</dependency>

bản dựng.

compile 'commons-validator:commons-validator:1.4.1'

Nhập khẩu:

import org.apache.commons.validator.routines.EmailValidator;

Mật mã:

String email = "myName@example.com";
boolean valid = EmailValidator.getInstance().isValid(email);

và cho phép địa chỉ địa phương

boolean allowLocal = true;
boolean valid = EmailValidator.getInstance(allowLocal).isValid(email);

2
Trong Android Studio, bạn có thể thêm biên dịch 'commons-validator: commons-validator: 1.4.1' vào ứng dụng của bạn \ build.gradle phụ thuộc {}
Stewiko99

2
Sau khi thực sự cố gắng xây dựng dự án của tôi, có vẻ như apache commons không hoạt động tốt với Android, hàng trăm cảnh báo và một số lỗi, thậm chí nó còn không được biên dịch. Đây là những gì tôi đã kết thúc bằng cách sử dụng howtodoinjava.com/2014/11/11/java-regex-validate-email-address
Stewiko99

1
Vấn đề tương tự với tôi là của Stewiko99. Sau khi thêm phần phụ thuộc, dự án sẽ không biên dịch, cho biết java.exe đã hoàn thành với mã thoát không bằng 0
Amit Mittal

1
Tôi cũng đã nhận được lỗi trong Android Studio. Tôi đã thay đổi từ 1.4.1 thành 1.5.1 và nó hoạt động!
Matt

1
Lưu ý: Use_the Emailvalidator trong org.apache.commons.validator.routines kể từ EmailValidator trong org.apache.commons.validator không được chấp nhận (Tôi đang sử dụng Trình xác thực 1.6 commons)
HopeKing 22/12/17

71

Trả lời muộn, nhưng tôi nghĩ nó đơn giản và xứng đáng:

    public boolean isValidEmailAddress(String email) {
           String ePattern = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$";
           java.util.regex.Pattern p = java.util.regex.Pattern.compile(ePattern);
           java.util.regex.Matcher m = p.matcher(email);
           return m.matches();
    }

Các trường hợp thử nghiệm :

nhập mô tả hình ảnh ở đây

Đối với mục đích sản xuất, xác thực tên miền nên được thực hiện theo mạng.


40
Đó là một trình xác nhận đơn giản khá tàn bạo mà bỏ qua hầu hết các quy tắc RFC cùng với IDN. Tôi sẽ tránh xa điều này cho bất kỳ ứng dụng chất lượng sản xuất nào.
mlaccetti

1
me@company.co.uk sẽ không hợp lệ ...
Alexander Burakevych

14
Đừng cuộn trình xác nhận dựa trên regex của riêng bạn cho những thứ được bao phủ bởi RFC.
Josh Glover

6
Phát minh lại bánh xe là được miễn là bạn không
bận

nó là tốt nhưng không phải cho tất cả các trường hợp.
Andrain

21

Nếu bạn đang cố gắng thực hiện xác nhận mẫu nhận được từ máy khách hoặc chỉ xác thực bằng bean - hãy giữ cho đơn giản. Tốt hơn là thực hiện xác thực email lỏng lẻo hơn là thực hiện nghiêm ngặt và từ chối một số người, (ví dụ: khi họ đang cố gắng đăng ký dịch vụ web của bạn). Với hầu hết mọi thứ được cho phép trong phần tên người dùng của email và rất nhiều tên miền mới được thêm vào mỗi tháng (ví dụ: công ty, .entreprise, .estate), sẽ an toàn hơn nếu không bị hạn chế:

Pattern pattern = Pattern.compile("^.+@.+\\..+$");
Matcher matcher = pattern.matcher(email);

3
Đây là một điểm thực sự tốt, bất kỳ ứng dụng hợp lý nào cũng cần có các biện pháp khác để ngăn chặn đầu vào này không bị khai thác xuống dòng nào
jmaculation

4
Làm thế nào về việc thay đổi nó thành "^. + @. + (\\. [^ \\.] +) + $" Để tránh dấu chấm?
Xingang Huang

7

Cuối câu hỏi, ở đây, nhưng: Tôi duy trì một lớp học tại địa chỉ này: http://lacinato.com/cm/software/emailrelated/emailaddress

Nó dựa trên lớp của Les Hazlewood, nhưng có nhiều cải tiến và sửa một vài lỗi. Giấy phép Apache.

Tôi tin rằng nó là trình phân tích cú pháp email có khả năng nhất trong Java và tôi chưa thấy một trình duyệt nào có khả năng hơn trong bất kỳ ngôn ngữ nào, mặc dù có thể có một trình duyệt ngoài đó. Nó không phải là trình phân tích cú pháp kiểu lexer, nhưng sử dụng một số biểu thức java phức tạp và do đó không hiệu quả như nó có thể, nhưng công ty của tôi đã phân tích tốt hơn 10 tỷ địa chỉ trong thế giới thực với nó: nó chắc chắn có thể sử dụng được trong hiệu suất cao tình hình. Có thể mỗi năm một lần nó sẽ đánh vào một địa chỉ gây ra lỗi tràn ngăn xếp regex (một cách thích hợp), nhưng đây là những địa chỉ spam dài hàng trăm hoặc hàng nghìn ký tự với nhiều dấu ngoặc kép và dấu ngoặc đơn và tương tự.

RFC 2822 và các thông số kỹ thuật liên quan thực sự khá dễ dãi về mặt địa chỉ email, vì vậy một lớp như thế này là quá mức cần thiết cho hầu hết các mục đích sử dụng. Ví dụ: sau đây là một địa chỉ hợp pháp, theo thông số kỹ thuật, khoảng trắng và tất cả:

"<bob \" (here) " < (hi there) "bob(the man)smith" (hi) @ (there) example.com (hello) > (again)

Không có máy chủ thư nào cho phép điều đó, nhưng lớp này có thể phân tích cú pháp (và viết lại thành dạng có thể sử dụng được).

Chúng tôi thấy các tùy chọn trình phân tích cú pháp email Java hiện tại không đủ bền (có nghĩa là tất cả chúng không thể phân tích một số địa chỉ hợp lệ), vì vậy chúng tôi đã tạo lớp này.

Mã này được ghi chép tốt và có nhiều tùy chọn dễ thay đổi để cho phép hoặc không cho phép một số biểu mẫu email nhất định. Nó cũng cung cấp rất nhiều phương thức để truy cập vào một số phần nhất định của địa chỉ (bên trái, bên phải, tên cá nhân, nhận xét, v.v.), để phân tích / xác thực các tiêu đề danh sách hộp thư, để phân tích / xác thực đường dẫn trở lại (là duy nhất trong số các tiêu đề), v.v.

Mã như được viết có một phụ thuộc javamail, nhưng thật dễ dàng để loại bỏ nếu bạn không muốn chức năng nhỏ mà nó cung cấp.


1
Xin chào, tôi đã sao chép nó vào GitHub để công khai cộng đồng nguồn mở. Bây giờ mọi người có thể bình luận, tài liệu và cải thiện mã. github.com/bbottema/email-rfc2822-validator . Tôi đã từng sử dụng phiên bản cũ của Les, nhưng tôi đã phải gỡ bỏ nó do lỗi đóng băng regex: leshazlewood.com/2006/11/06/emailaddress-java- class / từ
Benny Bottema

7

Tôi chỉ tự hỏi tại sao không ai nghĩ ra @Emailtừ các ràng buộc bổ sung của Trình xác thực Hibernate. Trình xác nhận chính nó là EmailValidator.


Mặc dù là một thay thế cho Apache commons, việc triển khai nó cũng thô sơ như hầu hết các thư viện dựa trên regex. Từ các tài liệu: "Tuy nhiên, như bài viết này thảo luận, không nhất thiết phải thực hiện trình xác nhận email tuân thủ 100%". Trình xác nhận toàn diện dựa trên regex duy nhất mà tôi biết là email-rfc2822-validator và nếu không thì EmailValidator4J có vẻ đầy hứa hẹn.
Benny Bottema

5

Les Hazlewood đã viết một lớp trình xác nhận email tuân thủ RFC 2822 rất kỹ lưỡng bằng cách sử dụng các biểu thức chính quy Java. Bạn có thể tìm thấy nó tại http: // www . Meatazlewood.com/?p=23 . Tuy nhiên, tính kỹ lưỡng của nó (hoặc triển khai Java RE) dẫn đến không hiệu quả - đọc các nhận xét về thời gian phân tích cú pháp cho các địa chỉ dài.


1
Tôi xây dựng trên lớp xuất sắc của Les Hazlewood (có một số lỗi). (Xem câu trả lời riêng của tôi cho câu hỏi này.) Mặc dù tôi đã duy trì phương thức regex java, chúng tôi sử dụng nó tốt trong môi trường quan trọng về hiệu năng. Nếu tất cả những gì bạn đang làm là phân tích địa chỉ, hiệu suất có thể là một vấn đề, nhưng đối với hầu hết người dùng, tôi nghi ngờ đó chỉ là khởi đầu của bất cứ điều gì họ đang làm. Các cập nhật của tôi cho lớp cũng đã khắc phục một số vấn đề đệ quy dài.
lacinato

Đây là một thư viện lỗi thời và đã được thay thế hai lần, cuối cùng bởi email-rfc2822-validator . Mặc dù nó vẫn phù hợp với tất cả các nhu cầu hiện đại, nhưng nó vẫn dễ bị các lỗi hiệu năng ẩn (và không hỗ trợ các thay đổi hạn chế của thông số RFC mới hơn).
Benny Bottema

3

Tôi đã chuyển một số mã trong Zend_Validator_Email:

@FacesValidator("emailValidator")
public class EmailAddressValidator implements Validator {

    private String localPart;
    private String hostName;
    private boolean domain = true;

    Locale locale;
    ResourceBundle bundle;

    private List<FacesMessage> messages = new ArrayList<FacesMessage>();

    private HostnameValidator hostnameValidator;

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        setOptions(component);
        String email    = (String) value;
        boolean result  = true;
        Pattern pattern = Pattern.compile("^(.+)@([^@]+[^.])$");
        Matcher matcher = pattern.matcher(email);

        locale = context.getViewRoot().getLocale();
        bundle = ResourceBundle.getBundle("com.myapp.resources.validationMessages", locale);

        boolean length = true;
        boolean local  = true;

        if (matcher.find()) {
            localPart   = matcher.group(1);
            hostName    = matcher.group(2);

            if (localPart.length() > 64 || hostName.length() > 255) {
                length          = false;
                addMessage("enterValidEmail", "email.AddressLengthExceeded");
            } 

            if (domain == true) {
                hostnameValidator = new HostnameValidator();
                hostnameValidator.validate(context, component, hostName);
            }

            local = validateLocalPart();

            if (local && length) {
                result = true;
            } else {
                result = false;
            }

        } else {
            result          = false;
            addMessage("enterValidEmail", "invalidEmailAddress");
        }

        if (result == false) {
            throw new ValidatorException(messages);
        }

    }

    private boolean validateLocalPart() {
        // First try to match the local part on the common dot-atom format
        boolean result = false;

        // Dot-atom characters are: 1*atext *("." 1*atext)
        // atext: ALPHA / DIGIT / and "!", "#", "$", "%", "&", "'", "*",
        //        "+", "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~"
        String atext = "a-zA-Z0-9\\u0021\\u0023\\u0024\\u0025\\u0026\\u0027\\u002a"
                + "\\u002b\\u002d\\u002f\\u003d\\u003f\\u005e\\u005f\\u0060\\u007b"
                + "\\u007c\\u007d\\u007e";
        Pattern regex = Pattern.compile("^["+atext+"]+(\\u002e+["+atext+"]+)*$");
        Matcher matcher = regex.matcher(localPart);
        if (matcher.find()) {
            result = true;
        } else {
            // Try quoted string format

            // Quoted-string characters are: DQUOTE *([FWS] qtext/quoted-pair) [FWS] DQUOTE
            // qtext: Non white space controls, and the rest of the US-ASCII characters not
            //   including "\" or the quote character
            String noWsCtl = "\\u0001-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f";
            String qText = noWsCtl + "\\u0021\\u0023-\\u005b\\u005d-\\u007e";
            String ws = "\\u0020\\u0009";

            regex = Pattern.compile("^\\u0022(["+ws+qText+"])*["+ws+"]?\\u0022$");
            matcher = regex.matcher(localPart);
            if (matcher.find()) {
                result = true;
            } else {
                addMessage("enterValidEmail", "email.AddressDotAtom");
                addMessage("enterValidEmail", "email.AddressQuotedString");
                addMessage("enterValidEmail", "email.AddressInvalidLocalPart");
            }
        }

        return result;
    }

    private void addMessage(String detail, String summary) {
        String detailMsg = bundle.getString(detail);
        String summaryMsg = bundle.getString(summary);
        messages.add(new FacesMessage(FacesMessage.SEVERITY_ERROR, summaryMsg, detailMsg));
    }

    private void setOptions(UIComponent component) {
        Boolean domainOption = Boolean.valueOf((String) component.getAttributes().get("domain"));
        //domain = (domainOption == null) ? true : domainOption.booleanValue();
    }
}

Với trình xác nhận tên máy chủ như sau:

@FacesValidator("hostNameValidator")
public class HostnameValidator implements Validator {

    private Locale locale;
    private ResourceBundle bundle;
    private List<FacesMessage> messages;
    private boolean checkTld = true;
    private boolean allowLocal = false;
    private boolean allowDNS = true;
    private String tld;
    private String[] validTlds = {"ac", "ad", "ae", "aero", "af", "ag", "ai",
        "al", "am", "an", "ao", "aq", "ar", "arpa", "as", "asia", "at", "au",
        "aw", "ax", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "biz",
        "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by", "bz", "ca",
        "cat", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co",
        "com", "coop", "cr", "cu", "cv", "cx", "cy", "cz", "de", "dj", "dk",
        "dm", "do", "dz", "ec", "edu", "ee", "eg", "er", "es", "et", "eu", "fi",
        "fj", "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh",
        "gi", "gl", "gm", "gn", "gov", "gp", "gq", "gr", "gs", "gt", "gu", "gw",
        "gy", "hk", "hm", "hn", "hr", "ht", "hu", "id", "ie", "il", "im", "in",
        "info", "int", "io", "iq", "ir", "is", "it", "je", "jm", "jo", "jobs",
        "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", "ky", "kz",
        "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma",
        "mc", "md", "me", "mg", "mh", "mil", "mk", "ml", "mm", "mn", "mo",
        "mobi", "mp", "mq", "mr", "ms", "mt", "mu", "museum", "mv", "mw", "mx",
        "my", "mz", "na", "name", "nc", "ne", "net", "nf", "ng", "ni", "nl",
        "no", "np", "nr", "nu", "nz", "om", "org", "pa", "pe", "pf", "pg", "ph",
        "pk", "pl", "pm", "pn", "pr", "pro", "ps", "pt", "pw", "py", "qa", "re",
        "ro", "rs", "ru", "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si",
        "sj", "sk", "sl", "sm", "sn", "so", "sr", "st", "su", "sv", "sy", "sz",
        "tc", "td", "tel", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to",
        "tp", "tr", "travel", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
        "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu", "wf", "ws",
        "ye", "yt", "yu", "za", "zm", "zw"};
    private Map<String, Map<Integer, Integer>> idnLength;

    private void init() {
        Map<Integer, Integer> biz = new HashMap<Integer, Integer>();
        biz.put(5, 17);
        biz.put(11, 15);
        biz.put(12, 20);

        Map<Integer, Integer> cn = new HashMap<Integer, Integer>();
        cn.put(1, 20);

        Map<Integer, Integer> com = new HashMap<Integer, Integer>();
        com.put(3, 17);
        com.put(5, 20);

        Map<Integer, Integer> hk = new HashMap<Integer, Integer>();
        hk.put(1, 15);

        Map<Integer, Integer> info = new HashMap<Integer, Integer>();
        info.put(4, 17);

        Map<Integer, Integer> kr = new HashMap<Integer, Integer>();
        kr.put(1, 17);

        Map<Integer, Integer> net = new HashMap<Integer, Integer>();
        net.put(3, 17);
        net.put(5, 20);

        Map<Integer, Integer> org = new HashMap<Integer, Integer>();
        org.put(6, 17);

        Map<Integer, Integer> tw = new HashMap<Integer, Integer>();
        tw.put(1, 20);

        Map<Integer, Integer> idn1 = new HashMap<Integer, Integer>();
        idn1.put(1, 20);

        Map<Integer, Integer> idn2 = new HashMap<Integer, Integer>();
        idn2.put(1, 20);

        Map<Integer, Integer> idn3 = new HashMap<Integer, Integer>();
        idn3.put(1, 20);

        Map<Integer, Integer> idn4 = new HashMap<Integer, Integer>();
        idn4.put(1, 20);

        idnLength = new HashMap<String, Map<Integer, Integer>>();

        idnLength.put("BIZ", biz);
        idnLength.put("CN", cn);
        idnLength.put("COM", com);
        idnLength.put("HK", hk);
        idnLength.put("INFO", info);
        idnLength.put("KR", kr);
        idnLength.put("NET", net);
        idnLength.put("ORG", org);
        idnLength.put("TW", tw);
        idnLength.put("ایران", idn1);
        idnLength.put("中国", idn2);
        idnLength.put("公司", idn3);
        idnLength.put("网络", idn4);

        messages = new ArrayList<FacesMessage>();
    }

    public HostnameValidator() {
        init();
    }

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        String hostName = (String) value;

        locale = context.getViewRoot().getLocale();
        bundle = ResourceBundle.getBundle("com.myapp.resources.validationMessages", locale);

        Pattern ipPattern = Pattern.compile("^[0-9a-f:\\.]*$", Pattern.CASE_INSENSITIVE);
        Matcher ipMatcher = ipPattern.matcher(hostName);
        if (ipMatcher.find()) {
            addMessage("hostname.IpAddressNotAllowed");
            throw new ValidatorException(messages);
        }

        boolean result = false;

        // removes last dot (.) from hostname 
        hostName = hostName.replaceAll("(\\.)+$", "");
        String[] domainParts = hostName.split("\\.");

        boolean status = false;

        // Check input against DNS hostname schema
        if ((domainParts.length > 1) && (hostName.length() > 4) && (hostName.length() < 255)) {
            status = false;

            dowhile:
            do {
                // First check TLD
                int lastIndex = domainParts.length - 1;
                String domainEnding = domainParts[lastIndex];
                Pattern tldRegex = Pattern.compile("([^.]{2,10})", Pattern.CASE_INSENSITIVE);
                Matcher tldMatcher = tldRegex.matcher(domainEnding);
                if (tldMatcher.find() || domainEnding.equals("ایران")
                        || domainEnding.equals("中国")
                        || domainEnding.equals("公司")
                        || domainEnding.equals("网络")) {



                    // Hostname characters are: *(label dot)(label dot label); max 254 chars
                    // label: id-prefix [*ldh{61} id-prefix]; max 63 chars
                    // id-prefix: alpha / digit
                    // ldh: alpha / digit / dash

                    // Match TLD against known list
                    tld = (String) tldMatcher.group(1).toLowerCase().trim();
                    if (checkTld == true) {
                        boolean foundTld = false;
                        for (int i = 0; i < validTlds.length; i++) {
                            if (tld.equals(validTlds[i])) {
                                foundTld = true;
                            }
                        }

                        if (foundTld == false) {
                            status = false;
                            addMessage("hostname.UnknownTld");
                            break dowhile;
                        }
                    }

                    /**
                     * Match against IDN hostnames
                     * Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames
                     */
                    List<String> regexChars = getIdnRegexChars();

                    // Check each hostname part
                    int check = 0;
                    for (String domainPart : domainParts) {
                        // Decode Punycode domainnames to IDN
                        if (domainPart.indexOf("xn--") == 0) {
                            domainPart = decodePunycode(domainPart.substring(4));
                        }

                        // Check dash (-) does not start, end or appear in 3rd and 4th positions
                        if (domainPart.indexOf("-") == 0
                                || (domainPart.length() > 2 && domainPart.indexOf("-", 2) == 2 && domainPart.indexOf("-", 3) == 3)
                                || (domainPart.indexOf("-") == (domainPart.length() - 1))) {
                            status = false;
                            addMessage("hostname.DashCharacter");
                            break dowhile;
                        }

                        // Check each domain part
                        boolean checked = false;

                        for (int key = 0; key < regexChars.size(); key++) {
                            String regexChar = regexChars.get(key);
                            Pattern regex = Pattern.compile(regexChar);
                            Matcher regexMatcher = regex.matcher(domainPart);
                            status = regexMatcher.find();
                            if (status) {
                                int length = 63;

                                if (idnLength.containsKey(tld.toUpperCase())
                                        && idnLength.get(tld.toUpperCase()).containsKey(key)) {
                                    length = idnLength.get(tld.toUpperCase()).get(key);
                                }

                                int utf8Length;
                                try {
                                    utf8Length = domainPart.getBytes("UTF8").length;
                                    if (utf8Length > length) {
                                        addMessage("hostname.InvalidHostname");
                                    } else {
                                        checked = true;
                                        break;
                                    }
                                } catch (UnsupportedEncodingException ex) {
                                    Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
                                }


                            }
                        }


                        if (checked) {
                            ++check;
                        }
                    }

                    // If one of the labels doesn't match, the hostname is invalid
                    if (check != domainParts.length) {
                        status = false;
                        addMessage("hostname.InvalidHostnameSchema");

                    }
                } else {
                    // Hostname not long enough
                    status = false;
                    addMessage("hostname.UndecipherableTld");
                }

            } while (false);

            if (status == true && allowDNS) {
                result = true;
            }

        } else if (allowDNS == true) {
            addMessage("hostname.InvalidHostname");
            throw new ValidatorException(messages);
        }

        // Check input against local network name schema;
        Pattern regexLocal = Pattern.compile("^(([a-zA-Z0-9\\x2d]{1,63}\\x2e)*[a-zA-Z0-9\\x2d]{1,63}){1,254}$", Pattern.CASE_INSENSITIVE);
        boolean checkLocal = regexLocal.matcher(hostName).find();
        if (allowLocal && !status) {
            if (checkLocal) {
                result = true;
            } else {
                // If the input does not pass as a local network name, add a message
                result = false;
                addMessage("hostname.InvalidLocalName");
            }
        }


        // If local network names are not allowed, add a message
        if (checkLocal && !allowLocal && !status) {
            result = false;
            addMessage("hostname.LocalNameNotAllowed");
        }

        if (result == false) {
            throw new ValidatorException(messages);
        }

    }

    private void addMessage(String msg) {
        String bundlMsg = bundle.getString(msg);
        messages.add(new FacesMessage(FacesMessage.SEVERITY_ERROR, bundlMsg, bundlMsg));
    }

    /**
     * Returns a list of regex patterns for the matched TLD
     * @param tld
     * @return 
     */
    private List<String> getIdnRegexChars() {
        List<String> regexChars = new ArrayList<String>();
        regexChars.add("^[a-z0-9\\x2d]{1,63}$");
        Document doc = null;
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);

        try {
            InputStream validIdns = getClass().getClassLoader().getResourceAsStream("com/myapp/resources/validIDNs_1.xml");
            DocumentBuilder builder = factory.newDocumentBuilder();
            doc = builder.parse(validIdns);
            doc.getDocumentElement().normalize();
        } catch (SAXException ex) {
            Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
        }

        // prepare XPath
        XPath xpath = XPathFactory.newInstance().newXPath();

        NodeList nodes = null;
        String xpathRoute = "//idn[tld=\'" + tld.toUpperCase() + "\']/pattern/text()";

        try {
            XPathExpression expr;
            expr = xpath.compile(xpathRoute);
            Object res = expr.evaluate(doc, XPathConstants.NODESET);
            nodes = (NodeList) res;
        } catch (XPathExpressionException ex) {
            Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
        }


        for (int i = 0; i < nodes.getLength(); i++) {
            regexChars.add(nodes.item(i).getNodeValue());
        }

        return regexChars;
    }

    /**
     * Decode Punycode string
     * @param encoded
     * @return 
         */
    private String decodePunycode(String encoded) {
        Pattern regex = Pattern.compile("([^a-z0-9\\x2d]{1,10})", Pattern.CASE_INSENSITIVE);
        Matcher matcher = regex.matcher(encoded);
        boolean found = matcher.find();

        if (encoded.isEmpty() || found) {
            // no punycode encoded string, return as is
            addMessage("hostname.CannotDecodePunycode");
            throw new ValidatorException(messages);
        }

        int separator = encoded.lastIndexOf("-");
            List<Integer> decoded = new ArrayList<Integer>();
        if (separator > 0) {
            for (int x = 0; x < separator; ++x) {
                decoded.add((int) encoded.charAt(x));
            }
        } else {
            addMessage("hostname.CannotDecodePunycode");
            throw new ValidatorException(messages);
        }

        int lengthd = decoded.size();
        int lengthe = encoded.length();

        // decoding
        boolean init = true;
        int base = 72;
        int index = 0;
        int ch = 0x80;

        int indexeStart = (separator == 1) ? (separator + 1) : 0;
        for (int indexe = indexeStart; indexe < lengthe; ++lengthd) {
            int oldIndex = index;
            int pos = 1;
            for (int key = 36; true; key += 36) {
                int hex = (int) encoded.charAt(indexe++);
                int digit = (hex - 48 < 10) ? hex - 22
                        : ((hex - 65 < 26) ? hex - 65
                        : ((hex - 97 < 26) ? hex - 97
                        : 36));

                index += digit * pos;
                int tag = (key <= base) ? 1 : ((key >= base + 26) ? 26 : (key - base));
                if (digit < tag) {
                    break;
                }
                pos = (int) (pos * (36 - tag));
            }
            int delta = (int) (init ? ((index - oldIndex) / 700) : ((index - oldIndex) / 2));
            delta += (int) (delta / (lengthd + 1));
            int key;
            for (key = 0; delta > 910; key += 36) {
                delta = (int) (delta / 35);
            }
            base = (int) (key + 36 * delta / (delta + 38));
            init = false;
            ch += (int) (index / (lengthd + 1));
            index %= (lengthd + 1);
            if (lengthd > 0) {
                for (int i = lengthd; i > index; i--) {
                    decoded.set(i, decoded.get(i - 1));
                }
            }

            decoded.set(index++, ch);
        }

        // convert decoded ucs4 to utf8 string
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < decoded.size(); i++) {
            int value = decoded.get(i);
            if (value < 128) {
                sb.append((char) value);
            } else if (value < (1 << 11)) {
                sb.append((char) (192 + (value >> 6)));
                sb.append((char) (128 + (value & 63)));
            } else if (value < (1 << 16)) {
                sb.append((char) (224 + (value >> 12)));
                sb.append((char) (128 + ((value >> 6) & 63)));
                sb.append((char) (128 + (value & 63)));
            } else if (value < (1 << 21)) {
                sb.append((char) (240 + (value >> 18)));
                sb.append((char) (128 + ((value >> 12) & 63)));
                sb.append((char) (128 + ((value >> 6) & 63)));
                sb.append((char) (128 + (value & 63)));
            } else {
                addMessage("hostname.CannotDecodePunycode");
                throw new ValidatorException(messages);
            }
        }

        return sb.toString();

    }

    /**
     * Eliminates empty values from input array
     * @param data
     * @return 
     */
    private String[] verifyArray(String[] data) {
        List<String> result = new ArrayList<String>();
        for (String s : data) {
            if (!s.equals("")) {
                result.add(s);
            }
        }

        return result.toArray(new String[result.size()]);
    }
}

Và một tệp IDIDNs hợp lệ với các mẫu biểu thức chính quy cho các tld khác nhau (quá lớn để bao gồm :)

<idnlist>
    <idn>
        <tld>AC</tld>
        <pattern>^[\u002d0-9a-zà-öø-ÿāăąćĉċčďđēėęěĝġģĥħīįĵķĺļľŀłńņňŋőœŕŗřśŝşšţťŧūŭůűųŵŷźżž]{1,63}$</pattern>
    </idn>
    <idn>
        <tld>AR</tld>
        <pattern>^[\u002d0-9a-zà-ãç-êìíñ-õü]{1,63}$</pattern>
    </idn>
    <idn>
        <tld>AS</tld>
        <pattern>/^[\u002d0-9a-zà-öø-ÿāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĸĺļľłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷźż]{1,63}$</pattern>
    </idn>
    <idn>
        <tld>AT</tld>
        <pattern>/^[\u002d0-9a-zà-öø-ÿœšž]{1,63}$</pattern>
    </idn>
    <idn>
        <tld>BIZ</tld>
        <pattern>^[\u002d0-9a-zäåæéöøü]{1,63}$</pattern>
        <pattern>^[\u002d0-9a-záéíñóúü]{1,63}$</pattern>
        <pattern>^[\u002d0-9a-záéíóöúüőű]{1,63}$</pattern>
    </id>
</idlist>

Câu trả lời này không còn áp dụng được nữa vì những lý do rõ ràng. Xóa xác thực TLD và có thể chấp nhận được nếu bạn muốn chấp nhận các địa chỉ email không phải tiếng Anh.
Christopher Schneider

3
public class Validations {

    private Pattern regexPattern;
    private Matcher regMatcher;

    public String validateEmailAddress(String emailAddress) {

        regexPattern = Pattern.compile("^[(a-zA-Z-0-9-\\_\\+\\.)]+@[(a-z-A-z)]+\\.[(a-zA-z)]{2,3}$");
        regMatcher   = regexPattern.matcher(emailAddress);
        if(regMatcher.matches()) {
            return "Valid Email Address";
        } else {
            return "Invalid Email Address";
        }
    }

    public String validateMobileNumber(String mobileNumber) {
        regexPattern = Pattern.compile("^\\+[0-9]{2,3}+-[0-9]{10}$");
        regMatcher   = regexPattern.matcher(mobileNumber);
        if(regMatcher.matches()) {
            return "Valid Mobile Number";
        } else {
            return "Invalid Mobile Number";
        }
    }

    public static void main(String[] args) {

        String emailAddress = "suryaprakash.pisay@gmail.com";
        String mobileNumber = "+91-9986571622";
        Validations validations = new Validations();
        System.out.println(validations.validateEmailAddress(emailAddress));
        System.out.println(validations.validateMobileNumber(mobileNumber));
    }
}

2

Nếu bạn đang tìm cách xác minh xem một địa chỉ email có hợp lệ hay không, thì VRFY sẽ giúp bạn tìm ra cách nào đó. Tôi thấy nó hữu ích cho việc xác thực mạng nội bộ địa chỉ (nghĩa là địa chỉ email cho các trang web nội bộ). Tuy nhiên, nó ít hữu ích hơn cho các máy chủ thư internet (xem phần lưu ý ở đầu trang này)


2

Mặc dù có nhiều lựa chọn thay thế cho commons Apache, nhưng việc triển khai của chúng là thô sơ nhất (như cách triển khai của Apache commons chính ) và thậm chí đã sai trong các trường hợp khác.

Tôi cũng sẽ tránh xa cái gọi là regex đơn giản 'không hạn chế'; không có những điều như vậy. Ví dụ @ được phép nhiều lần tùy thuộc vào ngữ cảnh, làm thế nào để bạn biết cái được yêu cầu ở đó? Regex đơn giản sẽ không hiểu nó, mặc dù email phải hợp lệ. Bất cứ điều gì phức tạp hơn đều trở nên dễ bị lỗi hoặc thậm chí có chứa các sát thủ hiệu suất ẩn . Làm thế nào bạn sẽ duy trì một cái gì đó như thế này ?

Trình xác nhận dựa trên regex tuân thủ RFC toàn diện duy nhất mà tôi biết là trình xác thực email-rfc2822 với biểu thức chính thức 'tinh chỉnh' của nó được đặt tên thích hợp Dragons.java . Mặc dù vậy, nó chỉ hỗ trợ thông số RFC-2822 cũ hơn , mặc dù đủ thích hợp cho các nhu cầu hiện đại ( cập nhật RFC-5322 nó trong các khu vực đã vượt quá phạm vi cho các trường hợp sử dụng hàng ngày).

Nhưng thực sự những gì bạn muốn là một lexer phân tích chính xác một chuỗi và chia nó thành cấu trúc thành phần theo ngữ pháp RFC. EmailValidator4J có vẻ đầy hứa hẹn về vấn đề đó, nhưng vẫn còn non trẻ và hạn chế.

Một tùy chọn khác mà bạn có là sử dụng dịch vụ web như dịch vụ web xác thực được kiểm tra trong trận chiến của Mailgun hoặc API Mailboxlayer (vừa lấy kết quả Google đầu tiên). Nó không hoàn toàn tuân thủ RFC, nhưng hoạt động đủ tốt cho các nhu cầu hiện đại.


1

Bạn muốn xác nhận những gì? Địa chỉ email?

Địa chỉ email chỉ có thể được kiểm tra sự phù hợp định dạng của nó. Xem tiêu chuẩn: RFC2822 . Cách tốt nhất để làm điều đó là một biểu thức thông thường. Bạn sẽ không bao giờ biết nếu thực sự tồn tại mà không gửi email.

Tôi đã kiểm tra trình xác nhận commons. Nó chứa một lớp org.apache.commons.validator.EmailValidator. Có vẻ là một điểm khởi đầu tốt.


Tôi không chắc chắn regex là cách tốt nhất để làm như vậy, nó khá khó đọc nếu bạn có ý định theo RFC đến thư
user2813274

Đồng ý với @ user2813274, bạn sẽ muốn có một lexer thích hợp, không phải spex regex.
Benny Bottema

1

Phiên bản Trình xác thực Apache Commons hiện tại là 1.3.1 .

Lớp xác nhận là org.apache.commons.validator.EmailValidator. Nó có một bản nhập cho org.apache.oro.text.perl.Perl5Util từ một dự án ORO Jakarta đã nghỉ hưu .

BTW, tôi thấy rằng có một phiên bản 1.4, đây là các tài liệu API . Trên trang web có ghi: "Xuất bản lần cuối: ngày 5 tháng 3 năm 2008 | Phiên bản: 1.4-SNAPSHOT", nhưng đó không phải là bản cuối cùng. Chỉ có cách xây dựng bản thân (nhưng đây là ảnh chụp nhanh, không phải ĐÁNG TIN CẬY) và sử dụng hoặc tải xuống từ đây . Điều này có nghĩa là 1.4 đã không được thực hiện cuối cùng trong ba năm (2008-2011). Đây không phải là phong cách của Apache. Tôi đang tìm kiếm một lựa chọn tốt hơn, nhưng không tìm thấy một lựa chọn nào được chấp nhận. Tôi muốn sử dụng một cái gì đó đã được thử nghiệm tốt, không muốn gặp bất kỳ lỗi nào.


1.4 SNAPSHOT cũng yêu cầu Jakarta ORO. Trình xác thực Apache Commons không thể sử dụng được cho tôi.
sương mù

Cuối cùng chọn Dr.Vet. Giải pháp Cumpanasu Florin: mkyong.com/THER-expressions/ từ
sương mù

1
Tôi đồng ý rằng trình xác nhận hợp lệ Apache Commons hoạt động tốt, nhưng tôi thấy nó khá chậm - hơn 3ms mỗi cuộc gọi.
Nic Cottrell

Hiệu suất không quan trọng đối với tôi.
sương mù

SNAPSHOT hiện tại (SVN REV 1227719 tính đến thời điểm hiện tại) không còn phụ thuộc bên ngoài như ORO nữa - bạn thậm chí không cần toàn bộ mô-đun xác nhận nữa - bốn lớp org.apache.commons.validator.routines.EmailValidator, InetAddressValidator, InetAddressValidator RegexValidator có thể đứng một mình
Jörg

0

Bạn cũng có thể muốn kiểm tra độ dài - email dài tối đa là 254 ký tự. Tôi sử dụng trình xác nhận apache commons và nó không kiểm tra điều này.


Các loài RFC 2821 (phần 4.5.3.1) chỉ định local-partchiều dài 64 và domainchiều dài 255. (Họ nói rằng thời gian dài hơn được cho phép có thể bị từ chối bởi phần mềm khác.)
sarnold 23/11/11

-2

Dường như không có bất kỳ thư viện hay cách hoàn hảo nào để tự làm việc này, trừ khi bạn phải gửi email đến địa chỉ email và chờ phản hồi (mặc dù điều này có thể không phải là một lựa chọn). Tôi đã kết thúc bằng cách sử dụng một đề xuất từ ​​đây http://blog.logichigh.com/2010/09/02/validating-an-e-mail-address/ và điều chỉnh mã để nó hoạt động trong Java.

public static boolean isValidEmailAddress(String email) {
    boolean stricterFilter = true; 
    String stricterFilterString = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
    String laxString = ".+@.+\\.[A-Za-z]{2}[A-Za-z]*";
    String emailRegex = stricterFilter ? stricterFilterString : laxString;
    java.util.regex.Pattern p = java.util.regex.Pattern.compile(emailRegex);
    java.util.regex.Matcher m = p.matcher(email);
    return m.matches();
}

-2

Đây là phương pháp tốt nhất:

public static boolean isValidEmail(String enteredEmail){
        String EMAIL_REGIX = "^[\\\\w!#$%&’*+/=?`{|}~^-]+(?:\\\\.[\\\\w!#$%&’*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\\\.)+[a-zA-Z]{2,6}$";
        Pattern pattern = Pattern.compile(EMAIL_REGIX);
        Matcher matcher = pattern.matcher(enteredEmail);
        return ((!enteredEmail.isEmpty()) && (enteredEmail!=null) && (matcher.matches()));
    }

Nguồn: - http://howtodoinjava.com/2014/11/11/java-regex-validate-email-address/

http://www.rfc-editor.org/rfc/rfc5322.txt


-2

Một tùy chọn khác là sử dụng trình xác nhận email Hibernate , sử dụng chú thích @Emailhoặc sử dụng lớp trình xác nhận theo chương trình, như:

import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator; 

class Validator {
    // code
    private boolean isValidEmail(String email) {
        EmailValidator emailValidator = new EmailValidator();
        return emailValidator.isValid(email, null);
    }

}

Tại sao các downvote? Đó là cùng một lớp được sử dụng bởi Trình xác thực Hibernate.
Dherik

-3

Đây là cách tiếp cận thực dụng của tôi, nơi tôi chỉ muốn các địa chỉ miền blah @ khác biệt hợp lý bằng cách sử dụng các ký tự được phép từ RFC. Địa chỉ phải được chuyển đổi thành chữ thường trước.

public class EmailAddressValidator {

    private static final String domainChars = "a-z0-9\\-";
    private static final String atomChars = "a-z0-9\\Q!#$%&'*+-/=?^_`{|}~\\E";
    private static final String emailRegex = "^" + dot(atomChars) + "@" + dot(domainChars) + "$";
    private static final Pattern emailPattern = Pattern.compile(emailRegex);

    private static String dot(String chars) {
        return "[" + chars + "]+(?:\\.[" + chars + "]+)*";
    }

    public static boolean isValidEmailAddress(String address) {
        return address != null && emailPattern.matcher(address).matches();
    }

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.