Qual é o melhor método de validação de endereço de email Java? [fechadas]

247

Quais são as boas bibliotecas de validação de endereço de email para Java? Existem alternativas para o validador do commons ?

jon077
fonte
15
Vou deixar isso aqui: davidcelis.com/blog/2012/09/06/…
mpenkov 30/10/12
Você não deve querer usar bibliotecas (ou expressões regulares) que não validam de maneira abrangente. Devido à complexidade do endereço de email válido, não há meio termo entre nenhuma validação e uma validação abrangente. A implementação do Apache Commons não é abrangente. Estou ciente de apenas uma biblioteca que é ( email-rfc2822-validator ), mas ainda funciona com grandes expressões regulares. Um lexer abrangente é o que você realmente deseja. EmailValidator4J diz que faz o trabalho, mas não tenho experiência com ele.
Benny Bottema
1
@BennyBottema Em vez de editar a pergunta com comentários, faça um post no Meta para discutir por que isso foi encerrado se você ainda tiver dúvidas.
Machavity

Respostas:

134

O Apache Commons é geralmente conhecido como um projeto sólido. No entanto, lembre-se de que você ainda precisará enviar um e-mail de verificação para o endereço se quiser garantir que seja um e-mail real e que o proprietário deseja que ele seja usado em seu site.

EDIT : houve um erro em que era muito restritivo no domínio, fazendo com que ele não aceitasse emails válidos de novos TLDs.

Este bug foi resolvido em 03 / Jan / 15 02:48 na versão 1.4.1 do commons-validator

Matthew Flaschen
fonte
1
Concordo com os bits extras que você citou, mas eles fazem parte do projeto de Validação do Commons?
31909 duffymo
2
Não, a EmailValidatorclasse Apache não envia uma mensagem de email para verificação.
Matthew Flaschen
3
Se o seu caso de uso é validar o endereço de email remoto de um usuário, esta solução possui uma falha considerável (semelhante a InternetAddress.validate ()): EmailValidator considera user @ [10.9.8.7] como um endereço de email válido - que eles estão de acordo com o RFC, mas talvez não seja para registro de usuário / formulário de contato.
Zillion1
1
@zillion, documentado no Apache COmmons: "Não é garantido que esta implementação capture todos os erros possíveis em um endereço de email." E eu disse o que você precisa fazer para "garantir que seja um email real". Porém, endereços com IPs locais podem ser válidos em ambientes raros.
Matthew Flaschen
5
O Apache Commons EmailValidator tem uma séria desvantagem: não suporta IDN.
Piohen
261

Usar o pacote oficial de email em java é o mais fácil:

public static boolean isValidEmailAddress(String email) {
   boolean result = true;
   try {
      InternetAddress emailAddr = new InternetAddress(email);
      emailAddr.validate();
   } catch (AddressException ex) {
      result = false;
   }
   return result;
}
Aaron Davidson
fonte
59
Observe que InternetAddress.validate () considera o usuário @ [10.9.8.7] e o usuário @ localhost como endereços de email válidos - que são de acordo com a RFC. Embora, dependendo do caso de uso (formulário da web), convém tratá-los como inválidos.
Zillion1
8
não apenas isso é válido como o @ zillion1 disse, mas também coisas como bla @ bla são consideradas válidas. Realmente não é a melhor solução.
Diego Plentz
4
@NicholasTolleyCottrell Este é Java, aqui nós jogamos e capturar exceções, eu realmente não entendo o seu ponto
gyorgyabraham
17
Suspeito que o construtor InternetAddress tenha sido violado. Ou meu sistema foi adulterado. Ou o RFC822 foi adulterado. Ou eu realmente poderia dormir um pouco agora. Mas eu apenas tentei algum código e as cinco seqüências a seguir passam como endereços de email válidos se você as passa para o construtor InternetAddress e, "claramente", elas não são válidas. Aqui vamos nós: ., .com, com., abce 123. Além disso, adicionar espaço em branco à esquerda ou à direita também não invalida as cadeias. Você é o juíz!
Martin Andersson
4
hum, queijo falha corretamente quando eu corro. a que diabos biblioteca javax.mail você está acessando ???
Aaron Davidson
91

O validador Apache Commons pode ser usado conforme mencionado nas outras respostas.

pom.xml:

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

build.gradle:

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

A importação:

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

O código:

String email = "[email protected]";
boolean valid = EmailValidator.getInstance().isValid(email);

e permitir endereços locais

boolean allowLocal = true;
boolean valid = EmailValidator.getInstance(allowLocal).isValid(email);
Aksel Willgert
fonte
2
No Android Studio, você pode adicionar a compilação 'commons-validator: commons-validator: 1.4.1' nas dependências do seu app \ build.gradle {}
Benjiko99
2
Depois de realmente tentar construir meu projeto, parece que o apache commons não funciona muito bem com o Android, centenas de avisos e alguns erros, nem mesmo compilou. Isto é o que eu acabei usando howtodoinjava.com/2014/11/11/java-regex-validate-email-address
Benjiko99
1
Mesmo problema comigo que o de Benjiko99. Depois de adicionar a dependência, o costume de compilação do projeto, diz java.exe acabado com código não saída de zero 2.
Amit Mittal
1
Eu também estava recebendo erros no Android Studio. Mudei de 1.4.1 para 1.5.1 e funciona!
Matt
1
Nota: Use_the EmailValidator em org.apache.commons.validator.routines desde EmailValidator em org.apache.commons.validator está obsoleta (estou usando 1.6 commons Validador)
HopeKing
71

Resposta tardia, mas acho que é simples e digna:

    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();
    }

Casos de teste :

insira a descrição da imagem aqui

Para fins de produção, as validações de nomes de domínio devem ser realizadas em rede.

Pujan Srivastava
fonte
40
Esse é um validador brutalmente simplista que ignora a maioria das regras de RFC junto com os IDNs. Eu evitaria isso para qualquer aplicativo de qualidade de produção.
mlaccetti
1
[email protected] não será válido ...
Alexander Burakevych
14
Não role seu próprio validador baseado em regex para itens cobertos por RFCs.
21713 Josh Glover
6
reinventar a roda é OK, desde que você não se importa o pneu furado ocasional
dldnh
é bom, mas não para todos os casos.
Andrain 2/02
21

Se você estiver tentando fazer uma validação de formulário recebida do cliente ou apenas uma validação de bean - mantenha-o simples. É melhor fazer uma validação de e-mail solta do que estrita e rejeitar algumas pessoas (por exemplo, quando elas estão tentando se registrar no seu serviço da web). Com quase tudo o que é permitido na parte do nome de usuário do email e tantos domínios novos sendo adicionados literalmente todos os meses (por exemplo, empresa, .entreprise, .estate), é mais seguro não ser restritivo:

Pattern pattern = Pattern.compile("^.+@.+\\..+$");
Matcher matcher = pattern.matcher(email);
Alexander Burakevych
fonte
3
esse é um ponto realmente bom, qualquer aplicativo razoável deve ter outras medidas para impedir que essa entrada seja explorada na linha de qualquer maneira
jmaculate
4
Que tal alterá-lo para "^. + @. + (\\. [^ \\.] +) + $") Para evitar um ponto final?
Xingang Huang
7

Atrasado para a pergunta, aqui, mas: Eu mantenho uma classe neste endereço: http://lacinato.com/cm/software/emailrelated/emailaddress

É baseado na classe de Les Hazlewood, mas possui inúmeras melhorias e corrige alguns bugs. Licença Apache.

Acredito que seja o analisador de email mais capaz em Java e ainda tenho que ver mais um em qualquer idioma, embora possa haver um por aí. Não é um analisador de estilo lexer, mas usa algum java regex complicado e, portanto, não é tão eficiente quanto poderia ser, mas minha empresa analisou mais de 10 bilhões de endereços do mundo real: é certamente utilizável em um alto desempenho situação. Talvez uma vez por ano, ele atinja um endereço que causa um estouro de pilha de expressões regulares (adequadamente), mas esses são endereços de spam com centenas ou milhares de caracteres com muitas aspas e parênteses.

O RFC 2822 e as especificações relacionadas são realmente bastante permissivas em termos de endereços de e-mail; portanto, uma classe como essa é um exagero para a maioria dos usos. Por exemplo, o seguinte é um endereço legítimo, de acordo com especificações, espaços e tudo:

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

Nenhum servidor de correio permitiria isso, mas essa classe pode analisá-lo (e reescrevê-lo para um formulário utilizável).

Descobrimos que as opções existentes do analisador de email Java são insuficientemente duráveis ​​(ou seja, todas elas não podem analisar alguns endereços válidos), por isso criamos essa classe.

O código está bem documentado e possui muitas opções fáceis de alterar para permitir ou não certos formulários de email. Ele também fornece muitos métodos para acessar determinadas partes do endereço (lado esquerdo, lado direito, nomes pessoais, comentários, etc.), para analisar / validar cabeçalhos da lista de caixas de correio, analisar / validar o caminho de retorno (que é único entre os cabeçalhos) e assim por diante.

O código escrito tem uma dependência de javamail, mas é fácil de remover se você não deseja a funcionalidade secundária que ele fornece.

lacinato
fonte
1
Olá, copiei no GitHub para a comunidade de código aberto pública. Agora todos podem comentar, documentar e aprimorar o código. github.com/bbottema/email-rfc2822-validator . Eu costumava usar a versão mais antiga por Les, mas eu tive que removê-lo devido a erros de congelamento regex: leshazlewood.com/2006/11/06/emailaddress-java-class/...
Benny Bottema
7

Só estou me perguntando por que ninguém surgiu com @Emailas restrições adicionais do Hibernate Validator. O validador em si é EmailValidator.

Markus Malkusch
fonte
Embora seja uma alternativa ao Apache commons, sua implementação é tão rudimentar quanto a maioria das bibliotecas baseadas em regex. Dos documentos: "No entanto, como este artigo discute, não é necessariamente prático implementar um validador de email 100% compatível". O único validador abrangente baseado em regex que conheço é o email-rfc2822-validator e, caso contrário, o EmailValidator4J parece promissor.
Benny Bottema
5

Les Hazlewood escreveu uma classe minuciosa de validador de e-mail compatível com RFC 2822 usando expressões regulares Java. Você pode encontrá-lo em http://www.leshazlewood.com/?p=23 . No entanto, sua abrangência (ou a implementação do Java RE) leva à ineficiência - leia os comentários sobre o tempo de análise para endereços longos.

Philip
fonte
1
Eu desenvolvi a excelente classe de Les Hazlewood (que tem alguns bugs). (Veja minha resposta separada para esta pergunta.) Embora eu tenha mantido o método java regex, nós o usamos muito bem em um ambiente crítico de desempenho. Se tudo o que você está fazendo é analisar endereços, o desempenho pode ser um problema, mas para a maioria dos usuários, suspeito que seja apenas o começo do que eles estão fazendo. Minhas atualizações na classe também corrigiram vários problemas de recursão longa.
lacinato
Esta é uma biblioteca desatualizada e foi substituída duas vezes, finalmente pelo email-rfc2822-validator . Embora ainda atenda a todas as necessidades modernas, também é propenso a erros de desempenho ocultos (e não suporta as alterações limitadas pelas especificações RFC mais recentes).
Benny Bottema
3

Portamos parte do código no 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();
    }
}

Com um validador de nome de host, da seguinte maneira:

@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()]);
    }
}

E um validIDNs.xml com padrões regex para os diferentes tlds (grandes demais para incluir :)

<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>
Erick Martinez
fonte
Esta resposta não é mais aplicável por razões óbvias. Remova a validação de TLD e provavelmente é aceitável se você deseja aceitar endereços de email em inglês.
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 = "[email protected]";
        String mobileNumber = "+91-9986571622";
        Validations validations = new Validations();
        System.out.println(validations.validateEmailAddress(emailAddress));
        System.out.println(validations.validateMobileNumber(mobileNumber));
    }
}
Suryaprakash Pisay
fonte
2

Se você deseja verificar se um endereço de e-mail é válido, o VRFY o ajudará em alguns aspectos . Descobri que é útil para validar endereços da intranet (ou seja, endereços de email para sites internos). No entanto, é menos útil para servidores de correio da Internet (veja as advertências na parte superior desta página)

Brian Agnew
fonte
2

Embora existam muitas alternativas para o Apache commons, suas implementações são rudimentares na melhor das hipóteses (como a própria implementação do Apache commons ) e até mesmo erradas em outros casos.

Eu também ficaria longe da regex chamada "não restritiva" simples; nao existe tal coisa. Por exemplo, @ é permitido várias vezes, dependendo do contexto, como você sabe que o requerido está lá? Regex simples não vai entender, mesmo que o email deva ser válido. Qualquer coisa mais complexa se torna propensa a erros ou até contém assassinos de desempenho ocultos . Como você está indo para manter algo como este ?

O único validador abrangente baseado em regex compatível com RFC que conheço é o email-rfc2822-validator com seu regex 'refinado' apropriadamente chamado Dragons.java . Porém, ele suporta apenas as especificações mais antigas da RFC-2822 , embora seja apropriado o suficiente para as necessidades modernas (a RFC-5322 atualiza -a em áreas já fora do escopo para casos de uso diários).

Mas, na verdade, o que você quer é um lexer que analise adequadamente uma string e a quebre na estrutura do componente, de acordo com a gramática RFC. O EmailValidator4J parece promissor a esse respeito, mas ainda é jovem e limitado.

Outra opção que você tem é usar um serviço da web, como o serviço da web de validação testado em batalha da Mailgun ou a API do Mailboxlayer (apenas obteve os primeiros resultados do Google). Não é estritamente compatível com RFC, mas funciona bem o suficiente para as necessidades modernas.

Benny Bottema
fonte
1

O que você deseja validar? O endereço de e-mail?

O endereço de email só pode ser verificado quanto à conformidade do formato. Veja o padrão: RFC2822 . A melhor maneira de fazer isso é uma expressão regular. Você nunca saberá se realmente existe sem enviar um email.

Eu verifiquei o validador do commons. Ele contém uma classe org.apache.commons.validator.EmailValidator. Parece ser um bom ponto de partida.

ReneS
fonte
Eu não tenho certeza que o regex é a melhor maneira de fazê-lo, é bastante ilegível se você pretende seguir a RFC ao pé da letra
user2813274
Concorde com @ user2813274, você desejaria um lexer adequado, não um espaguete regex.
Benny Bottema
1

A versão atual do Apache Commons Validator é 1.3.1 .

A classe que valida é org.apache.commons.validator.EmailValidator. Ele tem uma importação para org.apache.oro.text.perl.Perl5Util, que é de um projeto ORO de Jakarta aposentado .

BTW, eu descobri que existe uma versão 1.4, aqui estão os documentos da API . No site , diz: "Última publicação: 05 de março de 2008 | Versão: 1.4-INSTANTÂNEO", mas isso não é final. A única maneira de construir a si mesmo (mas este é um instantâneo, não o RELEASE) e usar ou fazer o download aqui . Isso significa que 1,4 não foi finalizado por três anos (2008-2011). Isso não está no estilo do Apache. Estou procurando uma opção melhor, mas não encontrei uma que seja muito adotada. Eu quero usar algo que é bem testado, não quero bater nenhum bug.

névoa
fonte
1.4 O INSTANTÂNEO também requer Jakarta ORO. O Apache Commons Validator não é utilizável para mim.
Nevo3
Finalmente, escolha o Dr.Vet. Solução de Cumpanasu Florin: mkyong.com/regular-expressions/…
mist
1
Concordo que o validador Apache Commons funciona bem, mas acho que é bastante lento - mais de 3ms por chamada.
Nic Cottrell
O desempenho não é tão importante para mim.
Nevoeiro
O SNAPSHOT do tronco atual (SVN REV 1227719 a partir de agora) não tem mais dependências externas como o ORO - você nem precisa mais de todo o módulo de validação - as quatro classes org.apache.commons.validator.routines.EmailValidator, InetAddressValidator, DomainValidator e RegExValidator são capazes de ficar sozinho
Jörg
0

Você também pode verificar o tamanho - os e-mails têm no máximo 254 caracteres. Eu uso o validador apache commons e ele não verifica isso.

minglis
fonte
RFC 2821 espécies (secção 4.5.3.1) especifica um local-partcomprimento de 64 e um domaincomprimento de 255. (Eles fazem dizer que já não é permitido pelo pode ser rejeitado por outro software.)
sarnold
-2

Parece não haver bibliotecas perfeitas ou maneiras de fazer isso sozinho, a menos que você tenha tempo para enviar um email para o endereço de email e aguardar uma resposta (talvez isso não seja uma opção). Acabei usando uma sugestão daqui http://blog.logichigh.com/2010/09/02/validating-an-e-mail-address/ e ajustando o código para que funcionasse em 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();
}
matt.writes.code
fonte
-2

Este é o melhor método:

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()));
    }

Fontes: - http://howtodoinjava.com/2014/11/11/java-regex-validate-email-address/

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

Pravinsingh Waghela
fonte
-2

Outra opção é usar o validador de email do Hibernate , usando a anotação @Emailou a classe do validador programaticamente, como:

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);
    }

}
Dherik
fonte
Por que o voto negativo? É a mesma classe usada pelo Hibernate Validator.
Dherik
-3

Aqui está minha abordagem pragmática, onde eu só quero endereços blah @ domain razoáveis ​​e distintos usando os caracteres permitidos do RFC. Os endereços devem ser convertidos para minúsculas com antecedência.

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();
    }

}
Craig Day
fonte