英文:
How to implement an email verification system with Go&MongoDB?
问题
我目前从我的/signup端点返回JWT令牌:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODMwMjcwOTUsInR5cGUiOiJhd2FpdGluZy12ZXJpZmljYXRpb24iLCJ1dWlkIjoiNjQ0ZmEzMTc1MmQwODdhYjg1NmE2NTA3In0.nHZg6RGkYxWM2vy4YyFHxyq_Snb3o9NpoRiMgEKnY2o
它的内容如下:
{
"exp": 1683027095,
"type": "awaiting-verification",
"uuid": "644fa31752d087ab856a6507"
}
我无法理解如何发送验证邮件。我计划创建一个名为"emailcodes"的单独集合,生成一个随机的6位数,比如123123,并将其插入到集合中,如下所示:
{
"uuid": "644fa31752d087ab856a6507",
"code": 123123,
"exp": 5分钟的UNIX时间戳
}
然后从白名单中获取代码(类型为awaiting-verification的令牌)的端点/code-verification。
问题是,我不知道如何发送电子邮件,而不会出现重复发送相同电子邮件等问题。有没有适用于GoLang的库可以实现这个功能?如果没有,我该如何实现一个系统,即使我也不会搞砸?
我已经使用https://pkg.go.dev/github.com/AfterShip/email-verifier?utm_source=godoc 验证了电子邮件的有效性和可达性。
..帮帮我?
英文:
I currently give JWT tokens from my /signup:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODMwMjcwOTUsInR5cGUiOiJhd2FpdGluZy12ZXJpZmljYXRpb24iLCJ1dWlkIjoiNjQ0ZmEzMTc1MmQwODdhYjg1NmE2NTA3In0.nHZg6RGkYxWM2vy4YyFHxyq_Snb3o9NpoRiMgEKnY2o
which is just:
{
"exp": 1683027095,
"type": "awaiting-verification",
"uuid": "644fa31752d087ab856a6507"
}
and can't wrap my head around how can I even send an email for verification. I planned to create a separate collection called "emailcodes", generate a random 6-digit number, let's say 123123, and insert into the collection like:
{
"uuid": "644fa31752d087ab856a6507"
"code": 123123
"exp": 5-minutes-in-unix
}
and take the code from whitelisted (tokens that is type is awaiting-verification) endpoint /code-verification
The problem is, I do not know how can I send emails without screwing everything up like sending the same email a dozen times, etc. Is there a GoLang library for this? If there isn't how can I implement a system for this that even I cant screw it up?
I already verify if the email is valid and reachable with https://pkg.go.dev/github.com/AfterShip/email-verifier?utm_source=godoc
..Help?
答案1
得分: 1
我认为你应该优先选择具有很好使用案例的良好参考资料,而不是解释。
在Go中发送邮件包
- Go的标准库肯定可以处理这个问题:https://pkg.go.dev/net/mail(但是,请也看看第二个选项)
- 我更喜欢使用这个第三方包,因为我觉得它更灵活和用户友好 - jordan-wright/email。此外,我使用这个包已经有一段时间了,没有遇到任何问题。所以,根据我作为一个Gopher的经验,我可以自信地推荐它。顺便说一句,如果你想尝试其他开源选项,还有很多其他选择。感谢Go庞大的社区!
实现电子邮件验证
- 首先,你需要实现邮件服务,然后再进行验证。这里是使用jordan-wright/email的示例。参考资料。
package mail
import (
"fmt"
"net/smtp"
"github.com/jordan-wright/email"
)
const (
smtpAuthAddress = "smtp.gmail.com"
smtpServerAddress = "smtp.gmail.com:587"
)
type EmailSender interface {
SendEmail(
subject string,
content string,
to []string,
cc []string,
bcc []string,
attachFiles []string,
) error
}
type GmailSender struct {
name string
fromEmailAddress string
fromEmailPassword string
}
func NewGmailSender(name string, fromEmailAddress string, fromEmailPassword string) EmailSender {
return &GmailSender{
name: name,
fromEmailAddress: fromEmailAddress,
fromEmailPassword: fromEmailPassword,
}
}
func (sender *GmailSender) SendEmail(
subject string,
content string,
to []string,
cc []string,
bcc []string,
attachFiles []string,
) error {
e := email.NewEmail()
e.From = fmt.Sprintf("%s <%s>", sender.name, sender.fromEmailAddress)
e.Subject = subject
e.HTML = []byte(content)
e.To = to
e.Cc = cc
e.Bcc = bcc
for _, f := range attachFiles {
_, err := e.AttachFile(f)
if err != nil {
return fmt.Errorf("failed to attach file %s: %w", f, err)
}
}
smtpAuth := smtp.PlainAuth("", sender.fromEmailAddress, sender.fromEmailPassword, smtpAuthAddress)
return e.Send(smtpServerAddress, smtpAuth)
}
-
电子邮件验证:这是一篇关于使用Go语言的电子邮件验证和密码重置流程的精彩博文,其中包含代码片段,我认为它可以比我现在能做到的更清楚地解释。所以我会引用它:使用golang进行电子邮件验证和密码重置流程。
-
如果你只需要检查电子邮件是否存在,而不是检查提供商用户的所有权,你可以简单地使用这个强大的Go包 - AfterShip/email-verifier。
英文:
Instead of explaining I think you should prefer good references with great use cases:
Mailing packages in Go
- Go's STD can handle this for sure: https://pkg.go.dev/net/mail (But, please have a look at the second option, too)
- I prefer to use this third-party package, coz I found it more flexible and user-friendly - jordan-wright/email. Additionally, I use this package for a while and got no issues with it. So, confidently recommended by my humble experience as a Gopher. BTW, there are too many other open-source options if you want to play with them. Thanks to the Go's great community!
Implementing email verification
- Firstly, you need to implement mailing service, and then verification. Here is the example using jordan-wright/email. Reference.
package mail
import (
"fmt"
"net/smtp"
"github.com/jordan-wright/email"
)
const (
smtpAuthAddress = "smtp.gmail.com"
smtpServerAddress = "smtp.gmail.com:587"
)
type EmailSender interface {
SendEmail(
subject string,
content string,
to []string,
cc []string,
bcc []string,
attachFiles []string,
) error
}
type GmailSender struct {
name string
fromEmailAddress string
fromEmailPassword string
}
func NewGmailSender(name string, fromEmailAddress string, fromEmailPassword string) EmailSender {
return &GmailSender{
name: name,
fromEmailAddress: fromEmailAddress,
fromEmailPassword: fromEmailPassword,
}
}
func (sender *GmailSender) SendEmail(
subject string,
content string,
to []string,
cc []string,
bcc []string,
attachFiles []string,
) error {
e := email.NewEmail()
e.From = fmt.Sprintf("%s <%s>", sender.name, sender.fromEmailAddress)
e.Subject = subject
e.HTML = []byte(content)
e.To = to
e.Cc = cc
e.Bcc = bcc
for _, f := range attachFiles {
_, err := e.AttachFile(f)
if err != nil {
return fmt.Errorf("failed to attach file %s: %w", f, err)
}
}
smtpAuth := smtp.PlainAuth("", sender.fromEmailAddress, sender.fromEmailPassword, smtpAuthAddress)
return e.Send(smtpServerAddress, smtpAuth)
}
-
Email verification: Here is a brilliant blog post about this flow with the snippets which I believe can explain more clearly then I can do so at this time, so I'd refernce that: Email Verification and Password Reset Flow using golang.
-
If u just need to check of an email exists or not instead of checking ownership on that for the provider user, you can simply use this powerful Go package - AfterShip/email-verifier.
答案2
得分: 0
有几个可用于发送电子邮件的库。
- Mailgun:https://github.com/mailgun/mailgun-go
- GoMail:https://github.com/go-gomail/gomail
- net/smtp:https://pkg.go.dev/net/smtp
以下是如何使用gomail.v2的示例代码:
package main
import (
"errors"
"fmt"
"gopkg.in/gomail.v2"
)
// EmailSender
type EmailSender struct {
Message *gomail.Message
dailer *gomail.Dialer
}
// NewEmailSender
func NewEmailSender() *EmailSender {
m := gomail.NewMessage()
dailer := gomail.NewDialer("smtp.gmail.com", 587, "smtpEmail@example.com", "smtp-password")
m.SetHeader("From", "from-email@gmail.com")
return &EmailSender{
Message: m,
dailer: dailer,
}
}
// Send
func (es *EmailSender) Send(subject, to, message string) error {
es.Message.SetHeader("To", to)
es.Message.SetHeader("Subject", subject)
es.Message.SetBody("text/plain", message)
if err := es.dailer.DialAndSend(es.Message); err != nil {
return err
}
return nil
}
// 发送电子邮件之前的检查
// - 验证码是否有效
// - 电子邮件是否已发送
// - 验证码是否过期
//
// TODO:实现你的检查逻辑
func ChecksBeforeEmailSend(code string) error {
return errors.New("电子邮件已发送")
}
func main() {
code := "123456"
emailer := NewEmailSender()
if err := ChecksBeforeEmailSend(code); err != nil {
panic(fmt.Sprintf("检查失败:%v", err.Error()))
}
err := emailer.Send(
"验证您的电子邮件", // 主题
"to@gmail.com", // 收件人邮箱
fmt.Sprintf("您的验证码是 %s", code), // 正文
)
if err != nil {
panic(err)
}
}
为了防止为验证码发送重复的电子邮件,可以在MongoDB集合中使用一个字段来指示该电子邮件是否已经发送给特定的用户和验证码,在发送电子邮件之前检查该字段的值。
例如:
```json
{
"uuid": "644fa31752d087ab856a6507",
"code": 123123,
"exp": 5-minutes-in-unix,
"sent": true
}
其中的"sent": true
表示已经为该用户和验证码发送了电子邮件。
我们可以在ChecksBeforeEmailSend
函数中添加其他检查逻辑,以在发送电子邮件之前进行检查。
英文:
There several libraries available to send emails.
- Mailgun : https://github.com/mailgun/mailgun-go
- GoMail : https://github.com/go-gomail/gomail
- net/smtp : https://pkg.go.dev/net/smtp
Here is an example how we can use gomail.v2
package main
import (
"errors"
"fmt"
"gopkg.in/gomail.v2"
)
// EmailSender
type EmailSender struct {
Message *gomail.Message
dailer *gomail.Dialer
}
// NewEmailSender
func NewEmailSender() *EmailSender {
m := gomail.NewMessage()
dailer := gomail.NewDialer("smtp.gmail.com", 587, "smtpEmail@example.com", "smtp-password")
m.SetHeader("From", "from-email@gmail.com")
return &EmailSender{
Message: m,
dailer: dailer,
}
}
// Send
func (es *EmailSender) Send(subject, to, message string) error {
es.Message.SetHeader("To", to)
es.Message.SetHeader("Subject", subject)
es.Message.SetBody("text/plain", message)
if err := es.dailer.DialAndSend(es.Message); err != nil {
return err
}
return nil
}
// checks before sending the email
// - code is valid
// - email already sent
// - code is expired or not
//
// TODO : Implement your checks
func ChecksBeforeEmailSend(code string) error {
return errors.New("email already sent")
}
func main() {
code := "123456"
emailer := NewEmailSender()
if err := ChecksBeforeEmailSend(code); err != nil {
panic(fmt.Sprintf("Check %v failed", err.Error()))
}
err := emailer.Send(
"Verify your email", // subject
"to@gmail.com", // to email
fmt.Sprintf("Your verfication code is %d", code), // message
)
if err != nil {
panic(err)
}
}
To prevent sending duplicate emails for a verification-code, you can use a field within the mongo db collection which will indicate that the email has already send for a particular user and code, check this field value before sending the email.
for example
{
"uuid": "644fa31752d087ab856a6507"
"code": 123123
"exp": 5-minutes-in-unix
"sent" : true
}
"sent" : true, indicates an email has already send for this user and code
We can add additional checks before sending the emails within ChecksBeforeEmailSend
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论