大家或许都用过网银,部分银行有一个动态口令验证码,一般为6位数字,其实其原理较为简单,即银行方保留了一个Key,同时动态口令生成器的机器中的key与银行方保持一致,通过OPT等协议算法生成6位code,其过程很难被逆转以及破解,因为通过算法,只要key设置得足够复杂,那么验证码几乎不可能被破解,同时基于时间策略,更难破解
准备工作
了解原理
- step1 时间范围(一般为1分钟) + 有效且复杂的 Key (字符串)
- step2 将字符串进行hash
- step3 转换为6位整数
- step4 服务器与客户端保持时间与算法以及key同步一致即可 (时间)
服务器端生成
通过服务器获取动态验证code,或通过离线加密程序获取动态验证code,如动态码生成机器、加密程序(无法看到加密密码即可)
const sm3 = require('sm3')
const dateFormat = require('dateformat')
const mobile = req.body.mobile
let start = dateFormat(Date.now(), 'yyyy-mm-dd-HH:MM')
let end = dateFormat(Date.now() + , 'yyyy-mm-dd-HH:MM')
let words = start + mobile + end + "密码&……*&@..."
const code = GENERATE_SIX_CODE(sm3(words))
return res.json({err: , code: code})
上面的时间段误差允许在1分钟之内,即验证码每分钟更新一次
离线客户端验证(主机应与服务器保持同步)
如果在docker中部署,需要注意的是,docker中的时间可能与宿主机不一样,所以需要先同步一下宿主机与docker时间,让虚拟机与宿主机时间一致;如果直接安装于宿主机,则让宿主机与服务器时间保持同步即可。
const sm3 = require('sm3')
const dateFormat = require('dateformat')
const code = req.body.code
const mobile = req.body.mobile
const start = dateFormat(Date.now(), 'yyyy-mm-dd-HH:MM')
const end = dateFormat(Date.now() + , 'yyyy-mm-dd-HH:MM')
let words1 = start + mobile + end + "密码&……*&@..."
User.findOne({mobile}, (err, user) => {
if (user) {
const code1 = GENERATE_SIX_CODE(sm3(words1))
if (code1 === code) {
req.logIn(user, function (err) {
if (err) {
return res.json({err: , msg: '登录失败'})
} else {
return res.json({err: })
}
})
}
} else {
return res.json({err: })
}
})
以上验证code一般写于离线客户端中,密码需要与服务器密码保持同步
扩展使用
当登录有多种权限的时候,比如管理员、学生、访客、老师等,我们可以在生成code的时候在密码中协定,如果是某种角色用谋者密码来加密,在验证的时候,我们额外生成多个匹配即可。
函数补充
用来生成6位整数
const murmurhash = require('node-murmurhash')
module.exports = {
GENERATE_SIX_CODE: (str) => {
let allToSix = (num, length) => {
return (new Array(length).join() + num).slice(-length)
}
let mhash = murmurhash(str)
if (mhash < ) mhash = mhash +
return allToSix(parseInt(mhash.toString().substr()), )
},
}