简介

在我们的实际开发当中我们会遇到使用腾讯云短信验证码或者阿里云短信验证码接口等等,这样就需要我们后端去处理这个请求和相关的逻辑判断,这个肯定是不能前端进行验证的,不然就等于直接告诉大家自己的腾讯云配置的secretId和secretKey。是十分的不安全。

开始

既然我们已经知道了具体要实现的功能,那么我们接下就是去实现这个功能。在实现的开发中也是要这样的,先分析功能做出实现此功能的架构,我们再去写代码,这样机会提高很多工作效率而且项目上线后也不会出现bug。

针对此功能我的实现逻辑设计是这样的

  1. 我们先对手机号进行验证判断是否是有效的手机号
  2. 使用math函数生成6位随机整数
  3. 请求腾讯云的短信服务接口给用户发送短信
  4. 将发送成功的短信存到redis中并在字段中设置number用于统计此手机号发送短信验证码的次数
  5. 然后在将发送成功的验证码存到session中,便于后面的登录判断使用

大概的实现逻辑我们清楚了,接下来就是敲代码环境

代码部分

对手机号的格式验证我们使用正则来进行匹配

const phone = req.body.phone //POST请求上来的手机号
phone.match(/^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/g)

如果手机号格式正确会返回一个数组,否则就是null或者空数组

接下来就是生成6位的随机整数

const code = Math.round(Math.random() * (899999) + 100000)
console.log(code)

调用腾讯云的短信接口,我们就可以参考腾讯云官方的示例

地址:https://cloud.tencent.com/document/product/382/43197#.E5.8F.91.E9.80.81.E7.9F.AD.E4.BF.A1

redis和session没啥好说的直接导入包,然后封装一下就可以了,下一篇文章我会放出来redis的配置和session的配置,这里就不多说废话了。

基本上需要的部分都搞定了接下来就是完整的实现代码
对手机号这种数据我们用过进行加密传参,所以我使用了base64加密

/** @Author: zhizhuo * @Date: 2022-03-25 10:08:57 * @Last Modified by: zhizhuo* @Last Modified time: 2022-03-25 14:24:30*/const models = require('../../models')
const tencentcloud = require("tencentcloud-sdk-nodejs")
const { setString, getString } = require('../../redis')
const Base64 = require('js-base64')
const moment = require('moment')
// 导入对应产品模块的client models。
const smsClient = tencentcloud.sms.v20210111.Client
//腾讯云短信配置文件,这些在腾讯云控制台都能看见
const config = {secretId: '',secretKey: '',SmsSdkAppId: "",SignName: "",TemplateId: "",Time: 5,timenow: moment().format('YYYYMMDD') + '00',timeend: moment().format('YYYYMMDD') + '23',time_now: moment().format("YYYY-MM-DD HH:mm:ss"),time_end: moment().format("YYYY-MM-DD") + ' 23:59:59'
}class Code {//获取短信验证码接口/发送短信async getcode(req, res) {try {const phone = Base64.decode(req.body.phone)const code = Math.round(Math.random() * (899999) + 100000)const time_key = moment(moment().format("YYYY-MM-DD") + ' 23:59:59').diff(moment(moment().format("YYYY-MM-DD HH:mm:ss")), 'seconds')if (!phone || phone.length != 11 || !phone.match(/^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/g) || phone.match(/^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/g)[0].length != 11) {var data = {code: 500,msg: '请输入手机号'}res.json(data)return}const client = new smsClient({credential: {secretId: config.secretId,secretKey: config.secretKey,},/* 必填:地域信息,可以直接填写字符串ap-guangzhou,支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */region: "ap-guangzhou",/* 非必填:* 客户端配置对象,可以指定超时时间等配置 */profile: {/* SDK默认用TC3-HMAC-SHA256进行签名,非必要请不要修改这个字段 */signMethod: "HmacSHA256",httpProfile: {/* SDK默认使用POST方法。* 如果你一定要使用GET方法,可以在这里设置。GET方法无法处理一些较大的请求 */reqMethod: "POST",/* SDK有默认的超时时间,非必要请不要进行调整* 如有需要请在代码中查阅以获取最新的默认值 */reqTimeout: 30,/*** 指定接入地域域名,默认就近地域接入域名为 sms.tencentcloudapi.com ,也支持指定地域域名访问,例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com*/endpoint: "sms.tencentcloudapi.com"},},})/* 请求参数,根据调用的接口和实际情况,可以进一步设置请求参数* 属性可能是基本类型,也可能引用了另一个数据结构* 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */// const params2 = {//     // 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666//     SmsSdkAppId: config.SmsSdkAppId,//     // 拉取最大条数,最多100条//     Limit: 100,// }// // 通过client对象调用想要访问的接口,需要传入请求对象以及响应回调函数// client.PullSmsSendStatus(params2, function (err, response) {//     // 请求异常返回,打印异常信息//     if (err) {//         console.log(err)//         return//     }//     // 请求正常返回,打印response对象//     console.log(response)//     res.json(response)// })/* 请求参数,根据调用的接口和实际情况,可以进一步设置请求参数* 属性可能是基本类型,也可能引用了另一个数据结构* 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */// const params1 = {//     // 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666//     SmsSdkAppId: config.SmsSdkAppId,//     // 拉取最大条数,最多100条//     Limit: 10,//     // 偏移量 注:目前固定设置为0//     Offset: 0,//     // 开始时间,yyyymmddhh 需要拉取的起始时间,精确到小时//     BeginTime: config.timenow,//     // 结束时间,yyyymmddhh 需要拉取的截止时间,精确到小时//     // 注:EndTime 必须大于 BeginTime//     EndTime: config.timeend,// }// 通过client对象调用想要访问的接口,需要传入请求对象以及响应回调函数// client.SendStatusStatistics(params1, function (err, response) {//     // 请求异常返回,打印异常信息//     if (err) {//         console.log(err)//         return//     }//     // 请求正常返回,打印response对象//     console.log(response)// })const params = {/* 短信应用ID: 短信SmsSdkAppId在 [短信控制台] 添加应用后生成的实际SmsSdkAppId,示例如1400006666 */// 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看SmsSdkAppId: config.SmsSdkAppId,/* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */// 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看SignName: config.SignName,/* 模板 ID: 必须填写已审核通过的模板 ID */// 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看TemplateId: config.TemplateId,/* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空 */TemplateParamSet: [code, config.Time],/* 下发手机号码,采用 e.164 标准,+[国家或地区码][手机号]* 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号*/PhoneNumberSet: ['+86' + phone],/* 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回 */SessionContext: "",/* 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手] */ExtendCode: "",/* 国际/港澳台短信 senderid(无需要可忽略): 国内短信填空,默认未开通,如需开通请联系 [腾讯云短信小助手] */SenderId: "",}//在redis中读取一下这个手机号是否发送过短信var getresult = JSON.parse(await getString(phone))const time_last = moment(moment().format("YYYY-MM-DD HH:mm:ss")).diff(moment(getresult.create_time), 'minutes')const again = 5 - moment(moment().format("YYYY-MM-DD HH:mm:ss")).diff(moment(getresult.create_time), 'minutes')if (time_last < 5) {var data = {code: 500,msg: '请过' + again + '分钟后再尝试'}res.json(data)return}if (!getresult || getresult.number > 5) {send_code(phone, code, getresult)} else {var data = {code: 500,msg: '此手机号今日获取验证码次数已达上线,请24小时后重试'}res.json(data)}// 通过client对象调用想要访问的接口,需要传入请求对象以及响应回调函数function send_code(phone, code, getresult) {client.SendSms(params, async function (err, response) {// 请求异常返回,打印异常信息// if (err) {//     console.log(err)//     return// }// 请求正常返回,打印response对象if (response.SendStatusSet[0].Code == 'Ok') {let number = 0if (getresult) {number = getresult.number + 1} else {number = 1}var code_data = {code: code,number: number,create_time: moment().format("YYYY-MM-DD HH:mm:ss"),}//在redis中统计此用户的请求次数var result = await setString(phone, JSON.stringify(code_data), time_key)//在session中设置验证码codeif (req.session.code) {delete req.session.code//销毁session让用户重新获取新的sessionreq.session.code = code // session 存储验证码数值} else {req.session.code = code // session 存储验证码数值}var data = {code: 200,msg: '发送成功'}res.json(data)} else {var data = {code: 500,msg: '发送失败,请稍后重试'}res.json(data)}})}} catch (error) {var data = {code: 500,msg: '异常请求'}res.json(data)}}
}module.exports = new Code()

我使用redis统计了该手机获取验证码的次数,用于判断是否正常的用于避免有人而已使用接口,导致短信资源被浪费

登录部分的验证:

/** @Author: zhizhuo * @Date: 2022-02-08 17:52:00 * @Last Modified by: zhizhuo* @Last Modified time: 2022-03-25 14:29:19*/const models = require('../../models')
const djangoHash = require('django-hash')
const createToken = require('../../token/token')
const aes = require('../../AES/index')
const moment = require('moment')
const Base64 = require('js-base64')class User {async login(req, res) {var username = Base64.decode(req.body.username)var password = Base64.decode(req.body.password)var code = Base64.decode(req.body.code)if (username && password) {if (username.length > 11 || username.length < 11 || !username.match(/^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/g) || username.match(/^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/g)[0].length != 11) {var data = {code: 500,msg: '账号输入有误,请重新输入'}delete req.session.code//销毁session让用户重新获取新的sessionreq.session.destroy()//销毁sessionres.json(data)} else if (password.length > 18) {var data = {code: 500,msg: '密码输入长度过长,请重新输入'}delete req.session.code//销毁session让用户重新获取新的sessionreq.session.destroy()//销毁sessionres.json(data)} else if (password.length < 6) {var data = {code: 500,msg: '密码输入长度过短,请重新输入'}delete req.session.code//销毁session让用户重新获取新的sessionreq.session.destroy()//销毁sessionres.json(data)} else if (!req.session.code) {var data = {code: 500,msg: '验证码已经过期请重新输入'}delete req.session.code//销毁session让用户重新获取新的sessionreq.session.destroy()//销毁sessionres.json(data)} else if (code = !req.session.code) {var data = {code: 500,msg: '验证码错误请重新输入'}delete req.session.code//销毁session让用户重新获取新的sessionreq.session.destroy()//销毁sessionres.json(data)}else {//手机号的AES加密var key = ''//自定义的key 16或者32位var iv = ''//位移偏亮自定义的var username_aes_result = aes.Encrypt(username, key, iv)var result = await models.auth_user.findAll({where: { is_superuser: 1, phone: username_aes_result },attributes: ['id', 'phone', 'password', 'last_login']})if (result && result.length > 0) {//登录密码的验证let passwordhash = result[0].passworddjangoHash.verify(password, passwordhash).then(async (resultpassword) => {if (resultpassword == true) {let time = moment().format("YYYY-MM-DD")var login_result = await models.auth_user.update({last_login: time}, {where: {phone: username_aes_result}});if (login_result && login_result[0] == 1) {var login_auth = 1var login_time = moment().format("YYYY-MM-DD HH:mm:ss")} else {var login_auth = 0var login_time = moment().format("YYYY-MM-DD HH:mm:ss")}var data = {code: 200,msg: '登录成功',login_num: login_auth,login_time: login_time,token: createToken({ id: result[0].id + 1000000 })}delete req.session.code//销毁session让用户重新获取新的sessionreq.session.destroy()//销毁sessionres.json(data)} else {var data = {code: 500,msg: '密码错误'}delete req.session.code//销毁session让用户重新获取新的sessionreq.session.destroy()//销毁sessionres.json(data)}}).catch(err => {var data = {code: 500,msg: '登录出错'}delete req.session.code//销毁session让用户重新获取新的sessionreq.session.destroy()//销毁sessionres.json(data)})} else {var data = {code: 500,msg: '当前账号无权登录'}delete req.session.code//销毁session让用户重新获取新的sessionreq.session.destroy()//销毁sessionres.json(data)}}} else {var data = {code: 500,msg: '请输入账号和密码'}delete req.session.code//销毁session让用户重新获取新的sessionreq.session.destroy()//销毁sessionres.json(data)}};module.exports = new User();

这里无论哪一步错误我都销毁session,防止验证码被重复使用,提高用户的安全性,可以很有效的避免爆破工具对用户的密码爆破。

最后

如果本篇文章对你有帮助请点个赞和收藏

Nodejs中如何调用腾讯云的短信验证码接口并对接口进行安全限制相关推荐

  1. 腾讯云发送短信验证码服务

    腾讯云发送短信验证码服务 1.注册腾讯云的账号 在腾讯云的官网:https://cloud.tencent.com/注册一个腾讯云的账号,就是日常的注册流程(这里就不贴图了),不过要实名认证啥的,认证 ...

  2. 腾讯云实现短信验证码登录

    腾讯云实现短信验证码登录 腾讯云配置 后端逻辑实现 腾讯云配置 首先在腾讯云搜索短信 然后按要求创建短信签名,短信模板.短信模板很容易审核,但是签名比较复杂,需要按要求填写和上传相关信息.如果驳回可以 ...

  3. java短信内容加链接_Java加腾讯云实现短信验证码功能

    准备工作 1.选择服务 2.添加应用:我已经添加完了 3.点击应用名称 4.出现ID和Key 5.编辑短信创建签名 6.在第5步后 使用到的jar包: qcloudsms-1.0.2.jar com. ...

  4. 使用腾讯云完成短信验证码登录功能

    首先导入依赖 在pom.xml中: <!-- 腾讯云的 --><dependency><groupId>com.tencentcloudapi</groupI ...

  5. Java集成腾讯云的短信验证码

    首先,你得在腾讯云上注册并开通短信服务. 第一步:创建短信应用 第二步:创建短信签名 第三步:在第二步成功后再创建短信模板 到这里就设置完成了,接下来是Java代码,通过腾讯给的java sdk接口实 ...

  6. thinkphp5使用腾讯云发送短信验证码服务

    1.打开腾讯云官网开通短信服务:https://console.cloud.tencent.com/sms/smslist,需要实名验证 2.下载相对应的sdk文件,我这里是php文件 3.解压后将以 ...

  7. Java结合腾讯云实现短信验证码的发送

    今天说说短信验证码 保姆级教程 由于现在阿里云个人账户是不允许个人账户申请签名和模板的,所有下面我们用腾讯云来实现发送验证的功能.首次注册白嫖200条国内短信 第一步: 申请签名:进入腾讯云官网,注册 ...

  8. TP6 腾讯云发送短信验证码配置详解

    一.发送注册验证码代码实现 参考腾讯云文档 https://cloud.tencent.com/document/product/382/56058 1.通过composer安装 composer r ...

  9. 腾讯云发送短信验证码

    腾讯云短信服务下载源码 maven配置 <dependency><groupId>com.github.qcloudsms</groupId><artifac ...

  10. 腾讯云sms短信验证码

    [短信业务属于腾讯云sms,国内短信免费试用,每月有100条] 腾讯云短信免费试用:https://cloud.tencent.com/product/csms 首先准备工作做好: 1.腾讯云短信功能 ...

最新文章

  1. 中国移动发布基于开源技术的大云系统
  2. 发轫大数据 文思海辉荣获IBM“灯塔奖”
  3. IoTSharp 2.0 发布
  4. jQuery中的渐变动画效果
  5. 基于etcd实现大规模服务治理应用实战
  6. 测绘——利用CASS及数据库批量导出/修改/更新地籍信息
  7. 用英语介绍计算机系统,如何用英语介绍计算机系统
  8. VS2013添加反编译工具-ILDasm
  9. PGP的安装及使用,利用加密软件PGP对邮件内容进行加密和解密,保姆级教学
  10. tomcat配置manger账户和host-manager账户的方法详细图解
  11. NavicatPremium从excel文件导入表数据
  12. 【数字华容道】一、核心
  13. Unity3D教程:手游开发常用排序算法 -下
  14. 自制紧张刺激的滑雪游戏,来一把?
  15. GAN生成对抗网络基础知识
  16. K8S之taint\cordon\uncordon\drain使用案例——筑梦之路
  17. 英语听力,口语常见的三个简读/略读/变读
  18. iphone 各个设备的分辨率和对应的像素尺寸
  19. ubuntu box_将您的Ubuntu Box转换成卡拉OK机
  20. LocalSend - 无需联网,开源跨平台的局域网文件互传工具(AirDrop 替代品)

热门文章

  1. mysql 创建数据库 utf8 命令_mysql创建数据库 utf8
  2. Taro小程序 Input组件focus属性失效解决方案
  3. UE4-蓝图-角色的移动,视角控制(五)人物走动到停下过度动画
  4. 儒家学派有哪些代表人物?
  5. laravel 框架使用hdjs 实现富文本编辑器功能
  6. java的学习内容,附高频面试题合集
  7. 科学计算机计算等比求和公式,等比数列求和公式
  8. 计算机网络原理实验实验一:使用网络协议分析仪Wireshark
  9. 软件测试背景目的要点概述
  10. 长方形内正方形Square