Introduction
SMS verification code is still a relatively common function, and there are already very mature solutions. This article introduces the SMS service of docking with Alibaba Cloud. A text message costs about two cents.
The basic process is:
- Go to the corresponding cloud platform to apply for permission, and the approximate cost is similar.
- Connect according to official documents and integrate into your own system
Let’s briefly talk about the business logic of the verification code. Points to note:
- SMS verification code validity period control, the setting here is
5
minutes - Randomly generated
6
verification code - The same mobile phone can only send a verification code once per minute.
- The same mobile phone number can only cache one verification code. If the previous verification code has not expired, it will be overwritten directly.
Alibaba Cloud platform service docking
The documentation related to Alibaba Cloud is quite complete. Just follow it. Here is a brief introduction.
- Register an Alibaba Cloud account
- Apply for SMS service test permission and bind a test mobile phone number
- Conduct a message sending test based on the information obtained after the application
AccessKeyId
,AccessKeySecret
,SignName
, and so on. The official example is as follows:TemplateCode
The importance of documentation should be done well even if you have the energy to do your own projects.
go code implementation
Test code, the local memory cache is used here, it is useless redis
. When using the following code, pay attention to the replacement marks TODO
.
SmsOperation
A is given interface
to facilitate subsequent docking with other platforms. The code directory structure is as follows:
- verifyCode
- aliyun.go
- sms.go
sms.go
package verifyCode
import (
"fmt"
"math/rand"
"time"
)
type SmsOperation interface {
SendVerificationCode(phoneNumber string) error
CheckVerificationCode(phoneNumber, verificationCode string) error
}
func NewSms() SmsOperation {
return getAliyunEntity()
}
// 创建6位随机数
func CreateRandCode() string {
return fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))
}
aliyun.go
The cache here can be a separate module
package verifyCode
import (
"errors"
openapi "github.com/alibabacloud-go/darabonba-openapi/client"
dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v2/client"
aliyunUtil "github.com/alibabacloud-go/tea-utils/service"
"github.com/alibabacloud-go/tea/tea"
"github.com/patrickmn/go-cache"
"sync"
"time"
)
type aliyun struct {
verificationCodeCache *cache.Cache // 验证码 5 分钟过期
verificationCodeReqCache *cache.Cache // 一分钟内只能发送一次验证码
}
var (
aliyunOnce sync.Once
aliyunEntity *aliyun
)
func getAliyunEntity() *aliyun {
aliyunOnce.Do(func() {
aliyunEntity = new(aliyun)
aliyunEntity.verificationCodeReqCache = cache.New(time.Minute, time.Minute)
aliyunEntity.verificationCodeCache = cache.New(time.Minute*5, time.Minute*5)
})
return aliyunEntity
}
func (this *aliyun) SendVerificationCode(phoneNumber string) (err error) {
// 验证是否可以获取验证码(1分钟有效期)
_, found := this.verificationCodeReqCache.Get(phoneNumber)
if found {
err = errors.New("请勿重复发送验证码")
return
}
// 生成验证码
verifyCode := CreateRandCode()
// 发送短信
err = this.SendSms(this.getVerifyCodeReq(phoneNumber, verifyCode))
if err != nil {
return
}
// 验证码加入缓存
this.verificationCodeReqCache.SetDefault(phoneNumber, 1)
this.verificationCodeCache.SetDefault(phoneNumber, verifyCode)
return
}
func (this *aliyun) CheckVerificationCode(phoneNumber, verificationCode string) (err error) {
cacheCode, found := this.verificationCodeCache.Get(phoneNumber)
if !found {
err = errors.New("验证码已失效")
return
}
cc, sure := cacheCode.(string)
if !sure {
err = errors.New("内部服务出错")
return
}
if cc != verificationCode {
err = errors.New("验证码输入错误")
return
}
return
}
// 可以上官网查看示例 https://next.api.aliyun.com/api/Dysmsapi/2017-05-25/SendSms?params={}
/**
* 使用AK&SK初始化账号Client
* @param accessKeyId
* @param accessKeySecret
* @return Client
* @throws Exception
*/
func (*aliyun) CreateClient(accessKeyId *string, accessKeySecret *string) (_result *dysmsapi20170525.Client, _err error) {
config := &openapi.Config{
// 您的 AccessKey ID
AccessKeyId: accessKeyId,
// 您的 AccessKey Secret
AccessKeySecret: accessKeySecret,
}
// 访问的域名
config.Endpoint = tea.String("dysmsapi.aliyuncs.com")
_result = &dysmsapi20170525.Client{
}
_result, _err = dysmsapi20170525.NewClient(config)
return _result, _err
}
func (this *aliyun) SendSms(req dysmsapi20170525.SendSmsRequest) (_err error) {
// TODO your key,from config
client, _err := this.CreateClient(tea.String("key id"), tea.String("key secret"))
if _err != nil {
return _err
}
defer func() {
if r := tea.Recover(recover()); r != nil {
_err = r
}
}()
runtime := &aliyunUtil.RuntimeOptions{
}
result, _err := client.SendSmsWithOptions(&req, runtime)
if _err != nil {
return _err
}
if *result.Body.Code != "OK" {
_err = errors.New(result.String())
return
}
return _err
}
func (this *aliyun) getVerifyCodeReq(phoneNumber, code string) (req dysmsapi20170525.SendSmsRequest) {
// TODO SignName TemplateCode
req = dysmsapi20170525.SendSmsRequest{
SignName: tea.String("SignName"),
TemplateCode: tea.String("TemplateCode"),
PhoneNumbers: tea.String(phoneNumber),
TemplateParam: tea.String(`{"code":"` + code + `"}`),
}
return
}
Summarize
- You should read the documentation of the connected SMS sending platform to learn the basic usage.
- Verification code caching, if it is a small business and does not have a large amount of data,
session
just put it in the service cache. If it is a distributed service or the amount of data is large, it is best to put itredis
in storage, which is a very mature solution. - Pay attention to protecting yourself from malicious attacks. After all, every text message you send is worth money.
reference
Alibaba Cloud's
best practice for sending SMS sample SMS verification codes