使用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()
}

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

相关推荐
百事老饼干2 分钟前
Java[面试题]-真实面试
java·开发语言·面试
customer0810 分钟前
【开源免费】基于SpringBoot+Vue.JS医院管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·开源·intellij-idea
2402_8575893620 分钟前
SpringBoot框架:作业管理技术新解
java·spring boot·后端
一只爱打拳的程序猿39 分钟前
【Spring】更加简单的将对象存入Spring中并使用
java·后端·spring
杨荧40 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
白子寰1 小时前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
王俊山IT1 小时前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
为将者,自当识天晓地。1 小时前
c++多线程
java·开发语言
小政爱学习!1 小时前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
k09331 小时前
sourceTree回滚版本到某次提交
开发语言·前端·javascript