天天看點

nodejs 基于時間的動态驗證碼登入、驗證等(離線模式可用)

大家或許都用過網銀,部分銀行有一個動态密碼驗證碼,一般為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()), )
    },

}