目前大多应用都是手机登录,但是作为开源的一个软件,或者是私有的一个应用,那么使用手机短信接收验证码成本比较高,使用邮箱相对更容易,
这里从tinode中取出发邮件的部分做一个测试,
其中邮箱一般需要设置后才能使用SMTP方式发送邮件,设置方式参考:
使用tinode架设自己的私有聊天服务
邮件其实是有格式的,不是随便发一个字符串过去就好了
func randomBoundary() string {var buf [24]byterand.Read(buf[:])return fmt.Sprintf("tinode--%x", buf[:])
}func GetMessage(SendFrom, to, title, dataType, content string) ([]byte, error) {message := &bytes.Buffer{}// Common headers.fmt.Fprintf(message, "From: %s\r\n", SendFrom)fmt.Fprintf(message, "To: %s\r\n", to)message.WriteString("Subject: ")// Old email clients may barf on UTF-8 strings.// Encode as quoted printable with 75-char strings separated by spaces, split by spaces, reassemble.message.WriteString(strings.Join(strings.Split(mime.QEncoding.Encode("utf-8", title), " "), "\r\n "))message.WriteString("\r\n")message.WriteString("MIME-version: 1.0;\r\n")if dataType == "plain" {// Plain text messagemessage.WriteString("Content-Type: text/plain; charset=\"UTF-8\"; format=flowed; delsp=yes\r\n")message.WriteString("Content-Transfer-Encoding: base64\r\n\r\n")b64w := base64.NewEncoder(base64.StdEncoding, message)b64w.Write([]byte(content))b64w.Close()} else if dataType == "html" {// HTML-formatted messagemessage.WriteString("Content-Type: text/html; charset=\"UTF-8\"\r\n")message.WriteString("Content-Transfer-Encoding: quoted-printable\r\n\r\n")qpw := qp.NewWriter(message)qpw.Write([]byte(content))qpw.Close()} else {// Multipart-alternative message includes both HTML and plain text components.boundary := randomBoundary()message.WriteString("Content-Type: multipart/alternative; boundary=\"" + boundary + "\"\r\n\r\n")message.WriteString("--" + boundary + "\r\n")message.WriteString("Content-Type: text/plain; charset=\"UTF-8\"; format=flowed; delsp=yes\r\n")message.WriteString("Content-Transfer-Encoding: base64\r\n\r\n")b64w := base64.NewEncoder(base64.StdEncoding, message)b64w.Write([]byte(content))b64w.Close()message.WriteString("\r\n")message.WriteString("--" + boundary + "\r\n")message.WriteString("Content-Type: text/html; charset=\"UTF-8\"\r\n")message.WriteString("Content-Transfer-Encoding: quoted-printable\r\n\r\n")qpw := qp.NewWriter(message)qpw.Write([]byte(content))qpw.Close()message.WriteString("\r\n--" + boundary + "--")}message.WriteString("\r\n")return message.Bytes(), nil
}
这里使用了用户名密码验证:
SMTP(Simple Mail Transfer Protocol)支持多种认证方式,其中常见的包括:
-
PLAIN: 客户端将用户名和密码以明文形式发送给服务器进行认证。这是一种最简单的认证方式,但是由于信息以明文形式传输,安全性较低。
-
LOGIN: 客户端发送用户名和密码,但是这次发送的是经过 BASE64 编码的形式。尽管没有以明文形式传输,但仍然不够安全,因为 BASE64 编码可以容易地解码。
-
CRAM-MD5: 客户端和服务器之间进行挑战-响应式的认证。服务器发送一个挑战给客户端,客户端使用密码进行哈希处理后的响应进行回复。这种方式更加安全,因为密码不会以明文形式传输。
import ("bytes""crypto/tls""encoding/base64""fmt""math/rand""mime"qp "mime/quotedprintable""net/smtp""strings"
)type Validator struct {SMTPAddr stringSMTPPort stringSMTPHeloHost stringTLSInsecureSkipVerify boolauth smtp.Auth
}type loginAuth struct {username, password []byte
}func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {return "LOGIN", []byte(a.username), nil
}// Next continues the authentication. Exported only to satisfy the interface definition.
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {if more {switch strings.ToLower(string(fromServer)) {case "username:":return a.username, nilcase "password:":return a.password, nildefault:return nil, fmt.Errorf("LOGIN AUTH unknown server response '%s'", string(fromServer))}}return nil, nil
}
发送一个邮件:
var user string = "test1@sina.com"
var touser string = "test1@qq.com"
var pass string = "1234566"func TestMail() {auth := &loginAuth{[]byte(user), []byte(pass)}v := Validator{"smtp.sina.com","25","sina.com",false,auth,}msg, _ := GetMessage(user, touser, "it is a test.", "plain", "这是一个测试")err := v.SendMail([]string{touser}, msg)fmt.Println(err)
}func (v *Validator) SendMail(rcpt []string, msg []byte) error {client, err := smtp.Dial(v.SMTPAddr + ":" + v.SMTPPort)if err != nil {return err}defer client.Close()if err = client.Hello(v.SMTPHeloHost); err != nil {return err}if istls, _ := client.Extension("STARTTLS"); istls {tlsConfig := &tls.Config{InsecureSkipVerify: v.TLSInsecureSkipVerify,ServerName: v.SMTPAddr,}if err = client.StartTLS(tlsConfig); err != nil {return err}}if v.auth != nil {if isauth, _ := client.Extension("AUTH"); isauth {err = client.Auth(v.auth)if err != nil {fmt.Println(err)return err}}}if err = client.Mail(user); err != nil {fmt.Println(err)return err}for _, to := range rcpt {if err = client.Rcpt(strings.ReplaceAll(strings.ReplaceAll(to, "\r", " "), "\n", " ")); err != nil {return err}}w, err := client.Data()if err != nil {return err}if _, err = w.Write(msg); err != nil {return err}if err = w.Close(); err != nil {return err}return client.Quit()
}
如果不报错,就去收件箱接收邮件就好了。