

Go语言 实现发送短信验证码 并登录
source link: https://studygolang.com/articles/35764
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Go语言 实现发送短信验证码 并登录
goCenter · 大约1分钟之前 · 84 次点击 · 预计阅读时间 8 分钟 · 大约8小时之前 开始浏览现在大多数app或wap都实现了通过手机号获取验证码进行验证登录,下面来看下用go来实现手机号发送短信验证码登录的过程,基于的框架是gin 。
首先是短信服务商的申请,比如腾讯云、阿里云、网易易盾等,腾讯云自己申请个微信公众号就行,然后申请相关的短信签名、和短信模板,腾讯有100条试用喔。
具体的代码实现。
配置腾讯云短信服务的发送短信相关配置,具体可以参考腾讯云短信服务的api 文档,进行配置。
sms:
secret-key: #秘钥,可在控制台查询
secret-id: #秘钥id ,可在控制台查询
sms-sdk-app-id: #应用id
Sign-name: #申请的签名
template-id: #模板id
go 这里采用的是viper进行加载配置,相关的加载配置代码如下
定义相关的配置结构体,并加载到整个项目的总的options
配置结构体中
// sms 发送短信的配置options
type SmsOptions struct {
SecretKey string `json:"secret-key,omitempty" mapstructure:"secret-key"`
SecretId string `json:"secret-id,omitempty" mapstructure:"secret-id"`
SmsSdkAppId string `json:"sms-sdk-app-id,omitempty" mapstructure:"sms-sdk-app-id"`
SignName string `json:"sign-name,omitempty" mapstructure:"sign-name"`
TemplateId string `json:"template-id,omitempty" mapstructure:"template-id"`
}
func NewSmsOptions() *SmsOptions {
return &SmsOptions{
SecretKey: "",
SecretId: "",
SmsSdkAppId: "",
SignName: "",
TemplateId: "",
}
}
// 这为项目总的一个options配置,项目启动的时候会将yaml中的加载到option中
type Options struct {
GenericServerRunOptions *genericoptions.ServerRunOptions `json:"server" mapstructure:"server"`
MySQLOptions *genericoptions.MySQLOptions `json:"mysql" mapstructure:"mysql"`
InsecuresServing *genericoptions.InsecureServerOptions `json:"insecure" mapstructure:"insecure"`
Log *logger.Options `json:"log" mapstructure:"log"`
RedisOptions *genericoptions.RedisOptions `json:"redis" mapstructure:"redis"`
SmsOptions *genericoptions.SmsOptions `json:"sms" mapstructure:"sms"`
}
func NewOptions() *Options {
o:=Options{
GenericServerRunOptions: genericoptions.NewServerRunOptions(),
MySQLOptions: genericoptions.NewMySQLOptions(),
InsecuresServing: genericoptions.NewInsecureServerOptions(),
RedisOptions: genericoptions.NewRedisOptions(),
Log: logger.NewOptions(),
SmsOptions: genericoptions.NewSmsOptions(),
}
return &o
}
viper加载配置的代码如下
func AddConfigToOptions(options *options.Options) error {
viper.SetConfigName("config")
viper.AddConfigPath("config/")
viper.SetConfigType("yaml")
err := viper.ReadInConfig()
if err != nil {
return err
}
optDecode := viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(mapstructure.StringToTimeDurationHookFunc(), StringToByteSizeHookFunc()))
err = viper.Unmarshal(options, optDecode)
fmt.Println(options)
if err != nil {
return err
}
return nil
}
func StringToByteSizeHookFunc() mapstructure.DecodeHookFunc {
return func(f reflect.Type,
t reflect.Type, data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(datasize.ByteSize(5)) {
return data, nil
}
raw := data.(string)
result := new(datasize.ByteSize)
result.UnmarshalText([]byte(raw))
return result.Bytes(), nil
}
}
下面是发送验证码的实现
type SmsClient struct {
Credential *common.Credential
Region string
Cpf *profile.ClientProfile
Request SmsRequest
}
type Option func(*SmsClient)
func NewSmsClient(options ...func(client *SmsClient)) *SmsClient {
client := &SmsClient{
Region: "ap-guangzhou",
Cpf: profile.NewClientProfile(),
}
for _, option := range options {
option(client)
}
return client
}
func WithRequest(request SmsRequest) Option {
return func(smsClient *SmsClient) {
smsClient.Request = request
}
}
func WithCredential(options options.SmsOptions) Option {
return func(smsClient *SmsClient) {
smsClient.Credential = common.NewCredential(options.SecretId, options.SecretKey)
}
}
func WithCpfReqMethod(method string) Option {
return func(smsClient *SmsClient) {
smsClient.Cpf.HttpProfile.ReqMethod = method
}
}
func WithCpfReqTimeout(timeout int) Option {
return func(smsClient *SmsClient) {
smsClient.Cpf.HttpProfile.ReqTimeout = timeout
}
}
func WithCpfSignMethod(method string) Option {
return func(smsClient *SmsClient) {
smsClient.Cpf.SignMethod = method
}
}
func (s *SmsClient) Send() bool {
sendClient, _ := sms.NewClient(s.Credential, s.Region, s.Cpf)
_, err := sendClient.SendSms(s.Request.request)
if _, ok := err.(*errors.TencentCloudSDKError); ok {
logger.Warnf("An API error has returned: %s", err)
return false
}
if err != nil {
logger.Warnf("发送短信失败:%s,requestId:%s", err)
return false
}
logger.Info("发送短信验证码成功")
return true
}
定义发送的client,这里采用function option 的编程模式来初始化发送的client.和发送的request,request的代码如下
type SmsRequest struct {
request *sms.SendSmsRequest
}
func NewSmsRequest(options *options.SmsOptions, withOptions ...func(smsRequest *SmsRequest)) *SmsRequest {
request := sms.NewSendSmsRequest()
request.SmsSdkAppId = &options.SmsSdkAppId
request.SignName = &options.SignName
request.TemplateId = &options.TemplateId
smsRequest := &SmsRequest{request: request}
for _, option := range withOptions {
option(smsRequest)
}
return smsRequest
}
type RequestOption func(*SmsRequest)
func WithPhoneNumberSet(phoneSet []string) RequestOption {
return func(smsRequest *SmsRequest) {
smsRequest.request.PhoneNumberSet = common.StringPtrs(phoneSet)
}
}
func WithTemplateParamSet(templateSet []string) RequestOption {
return func(smsRequest *SmsRequest) {
smsRequest.request.TemplateParamSet = common.StringPtrs(templateSet)
}
}
创建发送验证码的控制层,发送成功,并将此处的电话号码和验证码保存到redis缓存中,用来登录时候的验证码有效性的校验。
func (u *userService) SendPhoneCode(ctx context.Context, phone string) bool {
// 获取配置参数
smsSetting := global.TencenSmsSetting
phoneSet := []string{phone}
// 随机生成6位的验证码
var randCode string = fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))
templateSet := []string{randCode, "60"}
smsRequest := tencenSms.NewSmsRequest(smsSetting, tencenSms.WithPhoneNumberSet(phoneSet), tencenSms.WithTemplateParamSet(templateSet))
smsClient := tencenSms.NewSmsClient(tencenSms.WithRequest(*smsRequest), tencenSms.WithCredential(*smsSetting))
go smsClient.Send()
// 将验证码和手机号保存到redis中
_ = u.cache.UserCaches().SetSendPhoneCodeCache(ctx, phone, randCode)
return true
}
后面是通过手机验证码进行登录的流程
func (u *userService) LoginByPhoneCode(ctx context.Context, phone string, phoneCode string) (*model.User,error) {
// 从缓存中获取该手机号对应的验证码是否匹配
cacheCode, err :=u.cache.UserCaches().GetSendPhoneCodeFromCache(ctx,phone)
if err != nil {
return nil, errors.WithCode(code.ErrUserPhoneCodeExpire,err.Error())
}
if cacheCode!=phoneCode {
return nil,errors.WithCode(code.ErrUserPhoneCodeMiss,"")
}
return &model.User{
Nickname: "lala",
}, nil
}
参考链接:https://blog.csdn.net/leixiyu/article/details/123029204
Recommend
-
15
Android 几种发送短信的方法 – Android开发中文站android中发送短信很简单, 首先要在Mainfest.xml中加入所需要的权限: <uses-permission android:name="androi...
-
7
常见的告警方式有:邮件,电话,短信,微信。 短信和电话,通常是收费的(若你有不收费的,可以评论分享一下),而邮件又不是那么及时,因此最后我选择微信通知。 这里说的微信,是企业微信,而我之前用注册过个体户的执照,因此可以很轻松就可...
-
6
在技术上如何实现发送一条短信?我是3y,一年CRUD经验用十年的markdown程序员 常年被誉为优质八股文选手austin项目实现的第一个渠道::从发送短信开始01、短信介绍在项目介绍的时候,已...
-
11
巧用开源方案,零成本实现验证码短信转发 - 少数派 巧用开源方案,零成本实现验证码短信转发 Matrix 首页推荐Matrix 是少数派的写作社区,我们主张...
-
7
V2EX › Java 如何大量请求发送短信接口? Jekins · 1 天前 · 2026 次点击
-
11
移动发送奇怪短信?我想起了通讯行业的核弹级漏洞-51CTO.COM 移动发送奇怪短信?我想起了通讯行业的核弹级漏洞 作者:苏苏 2022-04-29 21:37:34 这个漏洞虽然存在已久,但是却一直在被...
-
23
V2EX › Android Pixel6a 无法发送短信 Tokyo101
-
14
V2EX › Telegram telegram 必须验证短信验证码登录了,不能用密码?可我没收到短信验证码啊
-
13
阿里云短信服务是一项基于云计算和大数据技术的企业级短信平台服务。它能够为企业和开发者提供高可用、高性能、高稳定性的短信发送服务,可以快速地将各类业务通知、验证码、营销推广等信息发送给用户。在我们经常登录一些系统或...
-
19
🔍 想知道YouTube验证码是否收费?快来看这个详细解析!💡 https://www.chenweiliang.com/cwl-30928.html 点击上方链接了解更多!👆
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK