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