注冊(cè)時(shí)如何進(jìn)行郵箱驗(yàn)證?

如果你的注冊(cè)流程需要一個(gè)簡(jiǎn)單的郵箱驗(yàn)證功能,或許我的思考能幫到你

僅僅驗(yàn)證郵箱是否存在?
注冊(cè)時(shí)我們需要保證用戶填寫(xiě)的郵箱信息有效,這時(shí)我們的思路很簡(jiǎn)單,就是想辦法驗(yàn)證郵箱是否真實(shí)存在,我去網(wǎng)上搜了搜,找到了我使用的第一個(gè)方法,使用rcpt to僅僅去驗(yàn)證郵箱是否存在

可以在點(diǎn)擊這里稍微學(xué)習(xí)一下
https://www.activexperts.com/...

從網(wǎng)上找到的部分對(duì)應(yīng)代碼

    public Response CheckEmailValidity(@RequestParam (value="email") String email) {

        String host = "";
        String hostName = email.split("@")[1];
        Record[] result = null;
        SMTPClient client = new SMTPClient();
        Response response = new Response();

        try {
            // find MX records
            Lookup lookup = new Lookup(hostName, Type.MX);
            lookup.run();
            if (lookup.getResult() != Lookup.SUCCESSFUL) {
                response.setCode(ResponseCode.FAIL);
                response.setMessage("The email is not exist");
                logger.debug(response.getMessage());
                return response;
            } else {
                result = lookup.getAnswers();
            }

            // connect to email server
            for (int i = 0; i < result.length; i++) {
                host = result[i].getAdditionalName().toString();
                client.connect(host);
                if (SMTPReply.isPositiveCompletion(client.getReplyCode())) {
                    break;
                }
                client.disconnect();
            }

            client.login("dashanju");
            client.setSender("123456@yahoo.com");
            client.addRecipient(email);
            if (250 == client.getReplyCode()) {
                response.setCode(ResponseCode.SUCCESS);
                response.setMessage("The email is exist");
            }else{
                response.setCode(ResponseCode.FAIL);
                response.setMessage("The email is not exist");
                logger.debug(response.getMessage());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                client.disconnect();
            } catch (IOException e) {
            }
        }
        return response;
    }

不足之處的思考
但是這種方法有一些弊端:

  • 我在測(cè)試的時(shí)候發(fā)現(xiàn)部分郵箱驗(yàn)證是有問(wèn)題的,比如123@qq.com,這個(gè)郵箱是不存在的,但是rcpt to的返回結(jié)果是true
  • 所以我們不僅需要驗(yàn)證郵箱是否真實(shí)存在,更需要驗(yàn)證這個(gè)郵箱是否是注冊(cè)者本人的郵箱,如果注冊(cè)者隨便填一個(gè)別人的郵箱,那就亂套了

第一個(gè)問(wèn)題,rcpt to確實(shí)無(wú)法保證做到100%的準(zhǔn)確度,對(duì)于這個(gè)問(wèn)題,我們可以靠其他技術(shù)來(lái)解決(JavaMailSender),這里我參考了這篇博客
https://blog.csdn.net/herui_M...

部分常見(jiàn)的網(wǎng)站是如何進(jìn)行郵箱驗(yàn)證的?
在思考第二個(gè)問(wèn)題之前,我們不妨思考一下我們平時(shí)注冊(cè)賬號(hào)的網(wǎng)站對(duì)于郵箱驗(yàn)證的流程是怎么樣的?在我們點(diǎn)擊注冊(cè)按鈕之前,還需要點(diǎn)擊一個(gè)驗(yàn)證郵箱的按鈕,只有自己的郵箱收到郵件,并且自己點(diǎn)擊郵件中的url,才會(huì)出現(xiàn)驗(yàn)證完后注冊(cè)頁(yè)面告訴我們的-“驗(yàn)證成功”

第二個(gè)問(wèn)題的大致思路,后端寫(xiě)三個(gè)接口
sendEmail,用來(lái)發(fā)送郵件
receiveEmail,接收用戶驗(yàn)證信息臨時(shí)存儲(chǔ)到緩存中,
checkEmail,查詢(xún)緩存,驗(yàn)證Email是否已被驗(yàn)證
初期的簡(jiǎn)單流程
整體的流程是這樣的:

  1. 前端驗(yàn)證郵箱格式后,請(qǐng)求send接口
  2. 后端send接口發(fā)送驗(yàn)證郵件(郵件中的url中應(yīng)該攜帶email信息,為了避免泄露信息,可以先對(duì)email的信息加密在進(jìn)行拼接)
  3. 用戶收到郵件,點(diǎn)擊鏈接訪問(wèn)
  4. 前端拿到加密過(guò)的email信息,去請(qǐng)求receive接口
  5. receive接口對(duì)加密的email進(jìn)行解密,然后判斷是否為郵箱格式,再存入Redis緩存中
  6. 前端訪問(wèn)check接口,查詢(xún)Redis緩存中是否有email信息

用戶之間的驗(yàn)證沖突
這樣看似已經(jīng)差不多可以了,但是還有一種情況——a注冊(cè)了b的郵箱
假設(shè)有a,b兩個(gè)用戶,同時(shí)注冊(cè),a填寫(xiě)了b的email信息,b也填寫(xiě)了b的email信息,顯然發(fā)送驗(yàn)證email信息的時(shí)候,這個(gè)郵件只有b真實(shí)能收到,b點(diǎn)了驗(yàn)證鏈接,a的流程卻搶先一步去查詢(xún)了Redis中email是否被驗(yàn)證,我們知道這個(gè)email是b所屬且b本人驗(yàn)證的,而在這個(gè)過(guò)程中a卻反客為主,將郵箱占為己有,所以這里的問(wèn)題就是,我們?nèi)绾伪WC,a申請(qǐng)的郵箱一定是a本人點(diǎn)擊確認(rèn)的,即前端申請(qǐng)email的人和緩存中點(diǎn)擊email確認(rèn)的人是一個(gè)人
進(jìn)一步思考后的整體流程
這個(gè)問(wèn)題的解決方法就是用一個(gè)唯一標(biāo)識(shí)符UUID來(lái)解決,我們對(duì)之前的流程重新思考一下

  1. 前端驗(yàn)證郵箱格式后,請(qǐng)求send接口
  2. 后端send接口發(fā)送郵件,郵件中的url應(yīng)該包含加密的email信息和UUID,并且應(yīng)該把UUID返回給前端
  3. 用戶收到send接口發(fā)來(lái)的郵件,點(diǎn)擊鏈接訪問(wèn)
  4. 前端收到了加密的email信息和UUID信息,加密的email信息不做處理,對(duì)此時(shí)收到的UUID和請(qǐng)求send接口時(shí)收到的UUID進(jìn)行比較,防止惡意訪問(wèn)(這也是為什么第二步send接口應(yīng)該返回給前端UUID的原因),驗(yàn)證通過(guò)后攜帶加密的email信息和UUID去請(qǐng)求receive接口
  5. receive接口收到信息后,對(duì)加密的email信息解密和格式驗(yàn)證,然后以鍵值對(duì)的形式將email-UUID存入Redis緩存中
  6. 前端訪問(wèn)check接口,攜帶email和UUID查詢(xún)緩存中是否存在,然后再去申請(qǐng)注冊(cè)

思考過(guò)程難免會(huì)有疏漏,歡迎指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容