短信验证模块
发布于 9 年前 作者 welchwsy 11287 次浏览 最后一次编辑是 8 年前 来自 分享

最近使用到手机短信校验,分享一下我的实现方式,也希望能够了解更好的实现方式 屏幕快照 2015-10-24 上午9.01.51.png 使用短信平台:阿里大鱼(阿里巴巴) 使用语言:node.js 数据库:redis(可用任意数据库或文件操作替代) 实现代码如下:

//安装依赖
//redis 数据库操作模块
npm install redis
//taobao API 操作模块。ES6请使用topapi-node
npm install top
//AuthController
var redisClient = require('../bin/redisClient');
var regx = /^(13|15|17|18|14)[0-9]{9}$/;
var top = require("top");
var client = top.createClient({
  appkey:'******',
  appsecret:'*******',
  REST_URL: 'http://gw.api.taobao.com/router/rest'
});
var params_check = {
      extend:"",
      sms_type:"normal",
      sms_free_sign_name:"注册验证",
      sms_param:'{"product":"网站名","code":"'+parseInt(code)+'"}',
      rec_num:tel,
      sms_template_code:"SMS_1020033"
    };

module.exports = {
  /**
   * 发送短信验证
   * @param req
   * @param res
   */
	"smsCheck":function(req,res){
    var tel = req.query.tel;
    if (!tel || !regx.exec(tel))return res.json({errMsg: "tel is no true", errCode: "400"});
    //生成4位数字的随机数
    var code = Math.floor(Math.random() * (9999 - 999 + 1) + 999);
    //检查用户是否已经注册
    User.findOne({tel: tel}).exec(function findOneCB(err, user) {
      if (user) return res.json({errMsg: "该手机号已被注册", errCode: "400"});
      redisClient.exists("register:" + tel, function (err, result) {
        if (err)return res.json({errMsg: "服务器出错,请重试", errCode: "500"});
        if (result == 1)return res.json({errMsg: "请求过于频繁,请稍候重试", errCode: "423"});
        //发送短信
        client.invoke('alibaba.aliqin.fc.sms.num.send', params_check, [], null, 'GET', function (err, result) {
          if (!err) {
            //发送成功
            redisClient.multi()
              //限制访问频率60秒
              .set("register:" + tel, code)
              .expire("register:" + tel, 60)
              .hset("code:" + tel, "code",code)
              .hset("code:" + tel,"count",0)
              .exec(function (err, replies) {
                if (!err)return res.json({errMsg: "ok", errCode: 0});
              });
          } else {
            res.json({errMsg: "服务器出错,请重试", errCode: "500"});
          }
        });
      });
    });
  },
  /**
   * 检验验证码
   * @param req
   * @param res
   */
  "checkRandom":function(req,res){
    var code = req.body.code;
    var tel = req.body.tel;
    redisClient.hgetall("code:"+tel,function(err,result){
      if (err)return res.status(500).json({errMsg:"服务器出错,请重试",errCode:"500"});
      //服务器不存在校验码或已被删除
      if(!result)return res.status(400).json({errMsg:"验证码失效,请重新获取验证码",errCode:"404"});
      if(result.code == code){
        return res.status(200).json({errMsg:"ok",errCode:"0"});
      }else if(result.code != code){
        if(result.count >=100)redisClient.del("code:" + tel);
        else redisClient.hset("code:" + tel,"count",Number(result.count)+1);
        return res.status(400).json({errMsg:"验证码不一致",errCode:"1"});
      }
    });
  },
  /**
   * 提交注册
   */
  "register":function(req,res){
    var data = req.body;
    if(!data || !data.tel ||!data.password || !data.code || !regx.exec(data.tel))return res.json({errMsg:"请输入有效内容",errCode:"400"});
    redisClient.get("code:"+data.tel,function(err,result){
      if (err)return res.json({errMsg:"服务器出错,请重试",errCode:"500"});
      if(result != data.code)  return res.json({errMsg:"验证码不一致",errCode:"1"});
      //通过短信校验
      var user = {
        tel:data.tel,
        password:data.password
      };
      User.create(user).exec(function createCB(err,createUser){
        if(err){
          return res.json({errMsg:"服务器出错,创建失败",errCode:500});
        }
        if(!err){
          req.session.objectid = createUser.id;
          return res.json({errMsg:"ok",errCode:0});
        }
        //清除缓存数据
        redisClient.del("code:"+data.tel);
      });
    });
  }
};
17 回复

我现在的项目还是邮箱验证,正在考虑换成短信验证。 考虑使用第三方短信平台 阿里大鱼(阿里巴巴) 是个不错的平台

今天偶然看到了这个帖子,有个问题想问下,失效时间设置的是1分钟,是不是失效时间太短了。是否应该设置两个时间,一个是验证码的失效时间,设置10分钟左右,一个时获取验证码的间隔时间设置成1分钟,如果1分钟内没有接收到验证码可以再获取比较合理啊?

@mehunk 那就失去了及时的意义了。1分钟是比较常见的,像otp里也可以这样玩的

@mehunk 1分钟失效的前提是 短信平台发送短信是即时稳定的,不会是你调用 api成功后过个5分钟用户才能收到。 而且你还要考虑到你服务器端与第三方服务之间的网络状态。所有考虑到这个问题可以适当延长失效时间

@mehunk 有道理,验证码时效性根据业务需求处理。code:phonehash类型,你可以调整过期时间并添加一个key=createTime,做处理就行了。考虑带阿里大鱼的稳定性,正常情况下1分钟可以的。

@i5ting 明白了,我还停留在几年前的验证码时代呢,那个时候可能是短信通道不稳定,验证码的有效时间比较长

redisClient 这个是什么包?网上没找到这个包

npm install redis
为什么不是 require(‘redis’) 而是 var redisClient = require(’…/bin/redisClient’); // Error: Cannot find module '…/bin/redisClient’ 这样不是会报错吗? 还要 exists 方法,貌似并没有这么个方法,我这么写了,结果是报错的。

@ruofeng086 redisClient是我配置的redis连接对象,简单点就是:

var redis = require("redis"),
var client = redis.createClient({
	host: '127.0.0.1',
	port: 6379
});
module.exports = client;

或者使用ioredis

var Redis = require('ioredis');
var redis = new Redis();
module.exports = redis;

exists现在还可以使用的 http://try.redis.io/

result.count >=100

验证码有效一分钟,可以尝试100次?

请教下,expire 设置了1分钟,到期自动删除了,所以发送成功的时候,就不用手动 del 了对吗?

@ruofeng086 短信的有效时间和尝试次数要根据你的业务设定,我只是设了一个我认为合理的值。设置了expire就不用自己手动del了。

@welchwsy 哈哈,人工在一分钟内跑一百次流程可能有些吃力呢

回到顶部