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

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

相关推荐
lemon_sjdk8 分钟前
ObjectProperty
java·开发语言·算法
大模型码小白14 分钟前
Spring AI Tool 实现自然语言操作 MySQL 数据库详解
服务器·开发语言·数据库·人工智能·python·mysql·spring
hh95018 分钟前
Agent Plan x DeepSeek Harness:Agent 供应链安全与第三方插件审计
java·开发语言·安全·agent plan·adg成都社区·adg社区
「QT(C++)开发工程师」42 分钟前
C++ std::move 详解
开发语言·c++
matlab代码1 小时前
基于matlab的玉米种子计数设计(GUI界面)【源码58期】
开发语言·matlab
c#上位机1 小时前
C#上位机项目实战——C#的dll项目编译时拷贝到指定目录下
开发语言·c#
Littlehero_1211 小时前
QT自定义控件之内嵌报表(4)(源码开源)
开发语言·qt
晓晓_za8986681 小时前
开源 GEO 优化源码二次开发:贴牌改造与业务模块扩展实践
java·运维·服务器·开发语言·性能优化·开源
++==1 小时前
RESTful详解:核心思想、框架、与HTTP的区别,API的设计风格
后端·http·restful
曹牧2 小时前
C#:数组与列表的差异
开发语言·c#