使用golang发送邮件

目前大多应用都是手机登录,但是作为开源的一个软件,或者是私有的一个应用,那么使用手机短信接收验证码成本比较高,使用邮箱相对更容易,

这里从tinode中取出发邮件的部分做一个测试,

其中邮箱一般需要设置后才能使用SMTP方式发送邮件,设置方式参考:

使用tinode架设自己的私有聊天服务

邮件其实是有格式的,不是随便发一个字符串过去就好了

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)支持多种认证方式,其中常见的包括:

  1. PLAIN: 客户端将用户名和密码以明文形式发送给服务器进行认证。这是一种最简单的认证方式,但是由于信息以明文形式传输,安全性较低。

  2. LOGIN: 客户端发送用户名和密码,但是这次发送的是经过 BASE64 编码的形式。尽管没有以明文形式传输,但仍然不够安全,因为 BASE64 编码可以容易地解码。

  3. 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()
}

如果不报错,就去收件箱接收邮件就好了。

相关推荐
一只自律的鸡9 分钟前
C项目 天天酷跑(下篇)
c语言·开发语言
源码哥_博纳软云12 分钟前
JAVA智慧养老养老护理帮忙代办陪诊陪护小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台
沐泽Mu16 分钟前
嵌入式学习-QT-Day05
开发语言·c++·qt·学习
小板凳-BGM27 分钟前
C# 第二阶段 modbus
开发语言·ui·c#
问道飞鱼29 分钟前
【Python知识】Python进阶-什么是装饰器?
开发语言·python·装饰器
长安——归故李31 分钟前
【C语言】成绩等级制
c语言·开发语言
追逐时光者34 分钟前
.NET 在 Visual Studio 中的高效编程技巧集
后端·.net·visual studio
黄金小码农36 分钟前
c# 2024/12/25 周三
开发语言·c#
大梦百万秋1 小时前
Spring Boot实战:构建一个简单的RESTful API
spring boot·后端·restful
忒可君1 小时前
C# winform 报错:类型“System.Int32”的对象无法转换为类型“System.Int16”。
java·开发语言