尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

邮箱验证实战:RFC 5322标准与三层验证体系

邮箱验证实战:RFC 5322标准与三层验证体系 写完这套邮箱验证我算是把网上的正则表达式都得罪了一遍。只要做过用户注册、找回密码、订阅推送你就躲不开邮箱验证这件事。说实话我见过太多项目上线前一晚,还在为一个邮箱正则改来改去的场景。更离谱的是有些同学直接从网上复制一段/^[\w\.-][\w\.-]\.\w$/就交差了结果到了测试阶段发现userexample被拦了usertagexample.com也收不到验证码又花一整天去调。这里面的核心问题不是正则写得好不好而是我们根本没搞清楚“邮箱验证”到底要验什么。按我的经验这件事至少可以拆成三个层面格式对不对Syntax、能不能收到信Deliverability、是不是你的Ownership。而 RFC 5322 标准就是解决第一个层面的金线也是后两个层面的地基。这篇内容我就把 RFC 5322 的标准细节、实战中的三层验证体系、以及我踩过的各种坑一次说清楚。1. 为什么“一个正则梭哈”的做法不靠谱1.1 邮箱地址的真实结构比你想的复杂很多人在设计验证规则时脑子里对邮箱地址的认知就是用户名域名六个字。但回到 RFC 5322 定义的标准里一个完整邮箱地址的可合法组成部分远超这个认知。标准的地址格式是local-partdomain。local-part在理论上允许出现几乎任意 ASCII 可打印字符包括大小写字母、数字、以及!#$%*/?^_{|}~-.这些特殊符号。更关键的是local-part还可以用双引号包裹起来里面甚至可以放空格和符号。比如john smithexample.com或者meworkcompany.com从语法标准上看是完全合法的。domain部分虽然相对规整但也支持 IP 地址字面量形式比如user[192.168.1.1]。这意味着什么意味着网上一搜一大把的那种“假标准正则”要么严格得离谱把合法地址拦在外面要么宽松得过头把这种垃圾当宝收进来。两者都会在实际业务里制造用户投诉和垃圾数据。1.2 严格语法验证的正则为什么难写理论上纯语法级的邮箱验证是可以用一个非常长的正则实现的。但问题是这个正则至少有上千个字符而且几乎没人能背下来维护起来是噩梦。更重要的是严格遵循 RFC 5322 的语法验证会把大量实际场景中根本不该收的地址也判为合法。比如ab这种单字母域名语法上可能通过其实RFC对域名的要求是至少一个标签但绝大多数情况它不是一个可以正常收发信的真实邮箱。所以我的判断很明确严格的 RFC 5322 语法正则只适合作为“格式门禁”不适合作为“唯一真理”。真正可落地的验证应该是一套组合策略而不是试图用一个万能正则解决所有问题的偷懒思路。1.3 从需求反推验证策略在实际项目里你需要先问自己一个问题我要防的是“用户手滑输错”还是“恶意灌水刷接口”这两个需求的解法路径完全不同。如果是手滑输错主要矛盾是用户体验不能因为一点格式瑕疵就把用户挡在门外要有包容度。如果是恶意灌水主要矛盾是系统安全拼的是限流、风控、邮件服务商的送达率而不是信不信你那个正则。理解了这两层区别你就不会再把时间浪费在“优化正则”这个低收益行为上而是会去搭一套分层验证体系。这也是我下面要展开的核心内容。2. RFC 5322 标准拆解到底哪些是硬规则哪些是软规则2.1 标准文档里的“应当”与“允许”RFC 5322 的前身是 RFC 822 和 RFC 2822它定义了互联网消息的格式。对开发者而言最值得关注的不是它几百页的“邮件头字段”定义而是消息地址mailbox的语法规则。这部分硬规则其实没有你想象的那么复杂我把常用要点整理如下部分允许内容常见误解local-part 常规字符大小写字母、数字、!#$%*-/?^_{|}~ 和点号误以为只能字母数字下划线local-part 点号规则点号不能连续出现也不能是首尾字符误以为可以在任意位置加点local-part 引号字符串双引号包裹后可含空格和特殊字符几乎没人支持这个场景domain 标签字母、数字、连字符连字符不能首尾误以为下划线合法domain 总长单个标签最长63字符域名总长最多255字符很少有人校验长度完整地址长度邮箱地址理论最长254字符很多库根本不限制从这张表就能看出来很多“标准”其实是被后人阉割过的。比如下划线在域名里RFC 5322 是不允许的但实际业务中很多内部系统的邮件地址确实会带下划线。你如果严格执行标准这些人就被卡死了。2.2 我对“遵守标准”的三点态度先说明我的立场我从来不在线上业务里用 100% 严格版 RFC 5322 正则。理由有三点。第一RFC 5322 管的是“消息格式标准”不是“用户输入有效性标准”。学术界和工业界公认真正严格的正则应采用“先拆分后逐段校验”的方式即解析器parser而不是裸正则。语言里直接可用的完备实现不多而且性能开销相对较大。第二过度严格的校验会伤害真实用户。我做过一个全球化产品上线第一周就收到日本用户反馈他的邮箱里包含号用的还是 Gmail 的别名功能结果被我们的“严谨正则”拦住了。这个问题你在 RFC 文档里找不到答案但真实世界的用户每天都在用。第三语法校验只是起点不是终点。一个邮箱即使 100% 符合 RFC 5322 语法也不代表它真实存在、能收信。真正决定成败的是后验环节——发送验证码、查看投递状态。所以我的策略是用 RFC 5322 做“减法”拦掉明显不合理的输入再用投递能力做“加法”确认这个地址真的可用。2.3 推荐的正则与库如果你问我到底该用哪个正则我会给你一个经过长期生产环境打磨的版本它比很多网上的版本要宽松但比“一个一个点”要严谨得多^[a-zA-Z0-9.!#$%*/?^_{|}~-][a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)$这个正则的核心思想是local-part 支持 RFC 5322 允许的绝大多数常规字符不包含引号字符串场景domain 部分严格按标签校验要求至少一个点。它不会把ab放进来也能接受usertaggmail.com。如果你不想手写正则可以直接用现成的库比如 Python 的email-validator或 JavaScript 的validator.js它们底层已经处理好了这些边界情况。用库还有一个额外好处它们通常会顺手做域名格式检查和常见拼写错误提示。比如用户输入gmai.comemail-validator会给出“你是不是想写 gmail.com”的提示。这个体验细节比你纠结正则效率有用得多。3. 可落地的三层验证体系从格式到归属3.1 第一层本地语法校验前端 后端语法校验放在前端的主要价值是“即时反馈”省得用户填完整个表单再被后端打回来。但请你务必明白前端校验只是用户体验的一部分不能作为安全边界。真正的语法校验必须后端再做一次。后端校验时要注意不能改用户的邮箱大小写local-part 理论上严格区分大小写虽然实际邮件系统几乎都按不区分处理不能擅自去除号后面的内容不能把用户输入的邮箱“修正”成你以为正确的样子。这些都是隐私和习惯问题不是技术问题。我做后端校验时会分三步走先用上面的正则做一次整体匹配。再做长度检查完整地址不超过 254 字符local-part 不超过 64 字符。最后拆出 domain 部分用IDNA库处理国际化域名——也就是把münchen.de转成xn--mnchen-3ya.de。这一步很多人会漏掉导致中文域名或带重音字符的域名验证失败。三步走完之后基本可以保证进入第二层的邮箱地址在“语法”层面没有硬伤了。3.2 第二层投递能力校验MX 记录与 SMTP 检查这一层是区分新手和老手的分水岭。语法合法了不代表这个邮箱真的能收信。比如no-such-userexample.com语法完全没毛病但发送验证码时 100% 会退信。投递能力校验的核心思路是先查这个域名的 MX 记录Mail Exchange 记录看看它有没有邮件服务器如果有再想想
返回列表