首页 后端

我们在注册网站的时候,通常会输入手机号并接收验证码进行手机号验证,这也是当今软件和网站增强安全性的一种有效手段。那么如果我们想要在注册的时候发送短信验证码,该怎么实现呢?这其实并不复杂,来看看吧!

⭐用到的软件及工具:
vscode、golang环境、gin框架、Redis数据库、阿里云服务框架

<!-- index-menu -->

一、调库

import "github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi"
import "github.com/garyburd/redigo/redis"

安装方法:在终端用cmd的cd命令进入src/github.com目录下:

go get github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi
go get github.com/garyburd/redigo/redis

二、在阿里云网站上的操作

阿里云官方教程文档:https://help.aliyun.com/document_detail/59210.html?spm=a2c4g.11174283.4.1.436f2c42BSfUpm

2.1 账号(注册 登录 实名认证 绑定支付宝)

这一步就跳过了,按着规矩来就好了

2.2 充值

如果是普通测试的话,不推荐开280和250元的那个(好贵呐),直接充值零钱扣费即可
在这里插入图片描述
在这里插入图片描述
不充值会怎么样?
会在测试的时候,报错:欠费(英文是啥 NOT_AMOUNT_ENOUGH 大概?)

2.3 申请签名和模板

附网址:https://dysms.console.aliyun.com/dysms.htm?spm=5176.12901015.0.i12901015.55ca525c8M47ub#/domestic/text/template
可能会等好一会儿审核...
在这里插入图片描述
在这里插入图片描述

2.4 AccessKey

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.5 快速学习 测试 API

在这里插入图片描述
我们可以查看API自定义模板,如果模板和签名的审核通过,是可以直接用的(而且注意一定是要等审核通过才可用)
在这里插入图片描述

这个模板的代码就不详细解释了,还是很好理解的,记得把accesskeyId和accessSecret换成AccessKey中的那两个
后端需要将PhoneNumbers->tel(接收的请求)code需要一个函数随机生成

三、代码逻辑(重难点)

这一块的逻辑考虑了两天,和部署的同学也交流了不少,最终决定用如下分离函数及Redis数据库解决。

3.1 发送短信函数

// 向手机发送验证码
func SendMsg(tel string, code string) string {
    client, err := dysmsapi.NewClientWithAccessKey("cn-hangzhou", "<accesskeyId>", "<accessSecret>")
    request := dysmsapi.CreateSendSmsRequest()
    request.Scheme = "https"
    request.PhoneNumbers = tel //手机号变量值
    request.SignName = "凌睿工作室" //签名
    request.TemplateCode = "SMS_19586XXXX" //模板编码
    request.TemplateParam = "{\"code\":\"" + code + "\"}"
    response, err := client.SendSms(request)
    fmt.Println(response.Code)
    if response.Code == "isv.BUSINESS_LIMIT_CONTROL" {
        return "frequency_limit"
    }
    if err != nil {
        fmt.Print(err.Error())
        return "failed"
    }
    return "success"
}

3.2 生成随机验证码

验证码是后端生成的,所以code是后端随机产生的一个六位数,代码如下:

// 随机验证码
func Code() string {
    rand.Seed(time.Now().UnixNano())
    code := rand.Intn(899999) + 100000
    res := strconv.Itoa(code) //转字符串返回
    return res
}

3.3 接收手机号并发送验证码

由于发送验证码需要手机号,所以这里将会从前端收手机号这一数据,然后发送验证码,并将userId-code以键值对的形式存储进Redis数据库。
为什么这样做???
因为在发送验证码的函数中用到了生成code的函数,然后在注册函数中需要对用户输入的那个验证码和后端生成的code进行匹配,而由于函数的独立性和局部变量的局限性,我们通过Redis键值对的方式缓冲手机号对应的code,从而实现判断验证码是否输入正确的功能。

// 接收手机号并发送验证码
func getValidationHandler(c *gin.Context) {
    var user User
    c.ShouldBind(&user)
    code := Code()
    fmt.Println(code)

    sendRes := SendMsg(user.Tel, code)
    if sendRes == "failed" {
        c.JSON(http.StatusInternalServerError, gin.H{
            "msg": "error",
        })
    } else {
        if !SetRedis(user.UserId, code) {
            c.JSON(http.StatusInternalServerError, gin.H{
                "msg": "error",
            })
        }
        c.JSON(http.StatusOK, gin.H{
            "msg": sendRes,
        })
    }
}

3.4 检查验证码是否正确函数

// 在注册时检查验证码
func Validation(validation string, userId string) int {
    var flag int
    getcode := GetRedis(userId)

    if validation == getcode {
        flag = 1
    } else {
        flag = 0
    }
    return flag
}

3.5 注册函数中的核心代码

func registerHandler(c *gin.Context) {
    var user User
    err := c.BindJSON(&user)
    if err != nil {
        fmt.Println(err)
        c.JSON(http.StatusBadRequest, gin.H{
            "msg": "error",
        })
        return
    }

    if Validation(user.Validation, user.UserId) == 0 {
        c.JSON(http.StatusOK, gin.H{
            "msg": "wrong_code",
        })
        return
    }
}

3.6 附Redis存储键值对(暂存userId-code)

在生成code的函数里存储,在检查函数里获取!!! 即可实现跨函数的变量值获取

func SetRedis(userId string, code string) bool {
    conn, err := redis.Dial("tcp", "127.0.0.1:6379")
    if err != nil {
        fmt.Println("connect redis error :", err)
        return false
    }
    defer conn.Close()
    _, err = conn.Do("SET", userId, code)
    if err != nil {
        fmt.Println("redis set error:", err)
        return false
    }
    _, err = conn.Do("expire", userId, 300)
    if err != nil {
        fmt.Println("set expire error: ", err)
        return false
    }
    return true
}

func GetRedis(userId string) string {
    conn, err := redis.Dial("tcp", "127.0.0.1:6379")
    if err != nil {
        fmt.Println("connect redis error :", err)
    }
    defer conn.Close()
    code, err := redis.String(conn.Do("GET", userId))
    if err != nil {
        fmt.Println("redis get error:", err)
    }
    return code
}

四、测试

输入手机号和用户名,发送请求到/Send,收到如下短信:
在这里插入图片描述
输入正确验证码:
在这里插入图片描述
输入错误验证码:
在这里插入图片描述
便不会进行下一步的注册了!
其实上面的的code_ok是不需要的!属于测试阶段的JSON字符。
总的来讲,注册时的逻辑是用户点击发送验证码发送请求(此时后端接收前端发送的用户名及手机号,将用户名与手机号以键值对的形式缓冲存储300s),然后用户输入收到的验证码,点击注册时先在注册函数检查输入验证码,如果通过就实现注册

PS. Redis数据库教程另附
参考资料:go语言之行--golang操作redis、mysql大全
Redis安装




文章评论

目录