人工智能通识程序员Golang 入门资料+笔记

软件技术-零基础-Golang用Hotmail发送验证邮件

2019-04-02  本文已影响11人  zhyuzh3d

欢迎关注我的专栏( つ•̀ω•́)つ【人工智能通识】


如何用Golang自动向用户邮箱发送验证码?

SMTP

Simple Mail Transfer Protocol,SMTP简单邮件传输协议,它是在网络传输电子邮件的常用标准。

我们的Golang可以通过SMTP方式调用右键服务商(比如Hotmail)的发邮件功能,替我们自动发邮件。

查看hotmail的SMTP设置

首先需要注册一个hotmail邮箱。

点这里注册Hotmail/Outlook邮箱

注册成功之后,右上角点齿轮弹出设置搜索,输入pop,点击进入POP和IMAP设置。

然后开启POP选项,注意下面的SMTP设置。

Golang的net/smtp

Golang提供了一个基本的SMTP发邮件模块,可以导入它import "net/smtp"

点这里看smtp的详细说明

它的基本流程是:

一般情况直接使用Golang的这个设置就可以了,但是Hotmail目前已经不再支持PlainAuth的认证模式,所以就要做单独的处理。

改名tool.go新增mail.go

在Golang里面,文件夹就是import的包名package,比如我们导入app/tool其实就是导入了tool文件夹下所有的.go文件,这些文件都必须以package tool开头,每个文件里的首字母大写的变量或者函数都会被放到tool.Xxx这样的二级命令里,并且如果文件里面有init函数也会被自动运行。

所以tool.go文件换成mongo.go也不会产生任何影响。那么我们就把它改名。

然后我们创建新的app/tool/mail.go文件,代码如下:

package tool

import (
    "net/smtp"
)

const umail string = "zhyuzhnd@hotmail.com"
const upw string = "zhyuzh3d"
const host string = "smtp.office365.com:587"

type loginAuth struct {
    username, password string
}

func genLoginAuth(username, password string) smtp.Auth {
    return &loginAuth{username, password}
}

func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
    return "LOGIN", []byte(a.username), nil
}

func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
    if more {
        switch string(fromServer) {
        case "Username:":
            return []byte(a.username), nil
        case "Password:":
            return []byte(a.password), nil
        }
    }
    return nil, nil
}

//SendMail 发送邮件
func SendMail(target string, body string, subject string) error {
    auth := genLoginAuth(umail, upw)

    contentType := "Content-Type: text/plain" + "; charset=UTF-8"
    msg := []byte("To: " + target +
        "\r\nFrom: " + umail +
        "\r\nSubject: " + subject +
        "\r\n" + contentType + "\r\n\r\n" +
        body)
    err := smtp.SendMail(host, auth, umail, []string{target}, msg)
    if err != nil {
        return err
    }
    return nil
}

这个代码看上去挺乱,但不必太理会它的意思,只要注意以下几点:

重新组织所有api

同样,我们也把api/login/login.goapi/register/register.go移动到api文件夹下面,把空的register文件夹和login文件夹删除。

这样我们可以在app.go中只导入一个src/api就可以了,并修改下面的服务接口设置:

    //API-注册登录相关
    http.HandleFunc("/api/register", api.Register)
    http.HandleFunc("/api/login", api.Login)

然后我们还要修改login.go中:

同样修改register.go:

然后测试运行,检查已有的旧功能是否正常。

修改注册页面register.html

我们在页面上增加验证码界面元素:

                <div class="form-group">
                    <label for="exampleInputPassword1">邮箱验证码:</label>
                    <div class="row">
                        <div class="col-7">
                            <input id='verify' onkeyup="checkVerify()"  class="form-control"
                                placeholder="请输入6位验证码">
                        </div>
                        <div class="col-5">
                            <button id='sendVerify' onClick="sendVerify()"
                                class="btn btn-success btn-block">发送验证码</button>
                        </div>
                    </div>
                    <small id='verifyTip' style="display:none">请输入6位数字验证码</small>
                </div>

利用Go Live查看效果大致是:

然后我们再修改下面的script部分,增加检查验证码格式的checkVerify和发送验证码邮件的sendVerify方法。

checkVerify部分:

    //检查验证码格式是否正确
    var verifyRe = /^[0-9]{6}$/

    function checkVerify() {
        var ver = $('#verify').val();
        if (verifyRe.test(ver) == false) {
            $('#verifyTip').css('display', 'block')
            $('#verify').removeClass('is-valid')
            $('#verify').addClass('is-invalid')
        } else {
            $('#verifyTip').css('display', 'none')
            $('#verify').removeClass('is-invalid')
            $('#verify').addClass('is-valid')
        }
        checkBtn()
    }

sendVerify部分:

    //发送验证邮件
    function sendVerify() {
        var data = {
            Email: $('#email').val(),
        }

        $.post('/api/sendRegVerifyMail', JSON.stringify(data), function (res) {
            alert(res.Msg);
        }, 'json')
    }

此外,我们还要修改checkBtn检测提交按钮的函数中应该加入验证码格式是否正确的检查:

//检查按钮是否可以被开启
    function checkBtn() {
        var agree = $('#agree').is(':checked');
        var mail = $('#email').val();
        var pw = $('#pw').val();
        var ver = $('#verify').val();
        if (pwRe.test(pw) && mailRe.test(mail) && verifyRe.test(ver) && agree) {
            $('#regBtn').removeAttr('disabled')
        } else {
            $('#regBtn').attr('disabled', 'true')
        }
    }

最后我们还要限制发送验证按钮只能在邮箱格式正确的时候才被启用,所以先在界面代码加入disabled禁止...class="btn btn-success btn-block" disabled="true">发送验证码</button>,然后在checkMail中对它进行开启或关闭:

    //检查邮箱的输入格式
    function checkMail() {
        var mail = $('#email').val();
        if (mailRe.test(mail) == false) {
            $('#mailTip').css('display', 'block')
            $('#email').removeClass('is-valid')
            $('#email').addClass('is-invalid')
            $('#sendVerify').attr('disabled', 'true')
        } else {
            $('#mailTip').css('display', 'none')
            $('#email').removeClass('is-invalid')
            $('#email').addClass('is-valid')
            $('#sendVerify').removeAttr('disabled')
        }
        checkBtn()
    }

增加sendRegVerifyMail.go

对应页面上的sendVerify方法的$.post('/api/sendRegVerifyMail',...,我们在Golang服务器上也应该增加对应的服务接口。

创建api/sendRegVerifyMail.go,其内容如下(我们已经拥有一个能发送邮件的tool.SendMail方法):

package api

import (
    "app/tool"
    "app/util"
    "context"
    "encoding/json"
    "fmt"
    "math/rand"
    "net/http"
    "regexp"
    "strconv"
    "time"

    "go.mongodb.org/mongo-driver/bson"
)

type sendRegVerifyMailReqDS struct {
    Email string
}

//SendRegVerifyMail 注册接口处理函数
func SendRegVerifyMail(w http.ResponseWriter, r *http.Request) {
    ds := sendRegVerifyMailReqDS{}
    json.NewDecoder(r.Body).Decode(&ds)

    mailRe, _ := regexp.Compile(`^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[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,}))$`)
    if !mailRe.MatchString(ds.Email) {
        util.WWrite(w, 1, "邮箱格式错误。", nil)
        return
    }

    //检查是否存在,如果已经存在且时间小于1分钟就就不再发送
    dbc := tool.MongoDBCLient.Database("myweb").Collection("regVerify")
    var u bson.M
    dbc.FindOne(context.TODO(), bson.M{"Email": ds.Email}).Decode(&u)
    now := time.Now().Unix()
    if u["Ts"] != nil && now-u["Ts"].(int64) < 60 {
        util.WWrite(w, 1, "请不要重复发送邮件。", nil)
        return
    }

    //生成随机6位数,并发送
    code := rand.Intn(899999) + 100000
    s := strconv.Itoa(code)
    err := tool.SendMail(ds.Email, "您在www.myweb.com的注册码是:"+s, "来自Myweb的注册验证码")
    if err != nil {
        util.WWrite(w, 1, "发送邮件失败。", nil)
        fmt.Println(err)
        return
    }

    //删除原有数据,创建新数据
    dbc.DeleteOne(context.TODO(), bson.M{"Email": ds.Email})
    dt := bson.M{"Code": s, "Email": ds.Email, "Ts": now}
    _, err = dbc.InsertOne(context.TODO(), dt)
    if err != nil {
        util.WWrite(w, 1, "写入数据库出错。", nil)
        fmt.Println(err)
    } else {
        util.WWrite(w, 0, "发送成功,请检查邮箱。", nil)
    }
    return
}

注意其中的几点:

写好这个文件,别忘了在app.go中添加服务路径:

http.HandleFunc("/api/sendRegVerifyMail", api.SendRegVerifyMail)

关于Hotmail邮箱

你必须要注册一个Hotmail邮箱才能借用它来发送邮件。

注册之后修改你mail.go里面的const umailcosnt upw

启动app.go之后尝试在注册页面上输入邮箱,然后点击发送验证码按钮,很可能会没有反应,Golang的输出上会出错,这是正常的,请在网页里检查你新注册的Hotmail邮箱,会收到一封提示邮件,这是要求你绑定手机号,绑定之后才可以正常发邮件。

在Hotmail网站绑定好手机之后,重新启动app.go,注册页中输入邮箱点击发送按钮,稍后能弹出发送成功,请检查邮箱。提示,如果连续点击则会提示请不要重复发送邮件。然后检查邮件,在垃圾邮件里面可以找到自己发送的内容:

注意Hotmail对这种用Golang的smtp.SendMail发送邮件的方式有限制,官方说法是每天不超过100封,但实际上可能十几封之后就被禁止发送了,需要你重新进入邮箱激活才能用,也可能今天就用不了了...遇到这个情况没有什么好办法,只能再去注册一个新邮箱测试了。

register.html中限制发送按钮

我们也可以在网页代码中限定用户每次点击发送按钮之后都冻结3秒,避免用户不小心连续点两下。

    //发送验证邮件
    function sendVerify() {
        var data = {
            Email: $('#email').val(),
        }

        $.post('/api/sendRegVerifyMail', JSON.stringify(data), function (res) {
            alert(res.Msg);
        }, 'json')
        $('#sendVerify').attr('disabled', 'true')
        setTimeout(() => {
            $('#sendVerify').removeAttr('disabled')
        }, 3000);        
    }

好了,差不多的时候可以切换到左侧的版本管理面板,输入框写点什么,然后Ctril+回车提交代码到Git,然后再从小菜单推送到Github。


如果弹出username和mail的提示,请切换到终端输入下面的命令(用户名和邮箱任意):

git config --global user.name "zhyuzh"
git config --global user.email "zhyuzh3d@hotmail.com"

几点补充

虽然我们基本上完成了注册功能,但还没有提供修改密码相关的功能,登录功能也不完善,稍后的文章我们会继续这些内容。


欢迎关注我的专栏( つ•̀ω•́)つ【人工智能通识】


每个人的智能新时代

如果您发现文章错误,请不吝留言指正;
如果您觉得有用,请点喜欢;
如果您觉得很有用,欢迎转载~


END

上一篇下一篇

猜你喜欢

热点阅读