目前大多应用都是手机登录,但是作为开源的一个软件,或者是私有的一个应用,那么使用手机短信接收验证码成本比较高,使用邮箱相对更容易,
这里从tinode中取出发邮件的部分做一个测试,
其中邮箱一般需要设置后才能使用SMTP方式发送邮件,设置方式参考:
邮件其实是有格式的,不是随便发一个字符串过去就好了
Go
func randomBoundary() string {
var buf [24]byte
rand.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 message
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()
} else if dataType == "html" {
// HTML-formatted message
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()
} 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: 客户端和服务器之间进行挑战-响应式的认证。服务器发送一个挑战给客户端,客户端使用密码进行哈希处理后的响应进行回复。这种方式更加安全,因为密码不会以明文形式传输。
Go
import (
"bytes"
"crypto/tls"
"encoding/base64"
"fmt"
"math/rand"
"mime"
qp "mime/quotedprintable"
"net/smtp"
"strings"
)
type Validator struct {
SMTPAddr string
SMTPPort string
SMTPHeloHost string
TLSInsecureSkipVerify bool
auth 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, nil
case "password:":
return a.password, nil
default:
return nil, fmt.Errorf("LOGIN AUTH unknown server response '%s'", string(fromServer))
}
}
return nil, nil
}
发送一个邮件:
Go
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()
}
如果不报错,就去收件箱接收邮件就好了。