一、前言
1、动态获取图片验证码
2、实现手机验证码登录(工具准备)
3、手机验证码登录(后台实现)
3、前台实现
二、主要内容
1、动态获取图片验证码
(1)请求的接口如下,返回的是一张svg的图片
## 获取一次性验证码 ### 请求URL: http://localhost:3000/captcha ### 请求方式:
(2)初次显示图片,可以直接在image中的src中请求路径直接得到
<input type="text" maxlength="11" placeholder="验证码" v-model="captche"> <img class="get_verification" src="http://localhost:4000/captcha" alt="captcha" @click='getCaptcha'>
(3)点击图片的时候更新,methods中调用方法
//获取图片验证码 getCaptcha(event){ console.log(this) console.log(event.target) event.target.src="http://localhost:4000/captcha?Time="+Date.now() }
2、实现手机验证码登录(工具准备)
(1)这里用到“容联通信云”:https://www.yuntongxun.com注册登录好之后,每个人都有不同的id号
(2)然后选择你要的验证类型
(3)你需要添加一个测试号码来接收验证短信
(4)接下来就可以根据官方文档写代码了
3、手机验证码登录(后台实现)
(1)后台项目结构如图所示
(2)发送shengchen
var md5 = require('blueimp-md5') var moment = require('moment') var Base64 = require('js-base64').Base64; var request = require('request'); /* 生成指定长度的随机数 */ function randomCode(length) { var chars = ['0','1','2','3','4','5','6','7','8','9']; var result = ""; //统一改名: alt + shift + R for(var i = 0; i < length ; i ++) { var index = Math.ceil(Math.random()*9); result += chars[index]; } return result; } // console.log(randomCode(6)); exports.randomCode = randomCode; /* 向指定号码发送指定验证码 */ function sendCode(phone, code, callback) { var ACCOUNT_SID = '8a216da86a960fd9016a96d0eb580180'; var AUTH_TOKEN = '3abf248c565446d0bf10d46eb62dee07'; var Rest_URL = 'https://app.cloopen.com:8883'; var AppID = '8a216da86a960fd9016a96d0eba80186'; //1. 准备请求url /* 1.使用MD5加密(账户Id + 账户授权令牌 + 时间戳)。其中账户Id和账户授权令牌根据url的验证级别对应主账户。 时间戳是当前系统时间,格式"yyyyMMddHHmmss"。时间戳有效时间为24小时,如:20140416142030 2.SigParameter参数需要大写,如不能写成sig=abcdefg而应该写成sig=ABCDEFG */ var sigParameter = ''; var time = moment().format('YYYYMMDDHHmmss'); sigParameter = md5(ACCOUNT_SID+AUTH_TOKEN+time); var url = Rest_URL+'/2013-12-26/Accounts/'+ACCOUNT_SID+'/SMS/TemplateSMS?sig='+sigParameter; //2. 准备请求体 var body = { to : phone, appId : AppID, templateId : '1', "datas":[code,"1"] } //body = JSON.stringify(body); //3. 准备请求头 /* 1.使用Base64编码(账户Id + 冒号 + 时间戳)其中账户Id根据url的验证级别对应主账户 2.冒号为英文冒号 3.时间戳是当前系统时间,格式"yyyyMMddHHmmss",需与SigParameter中时间戳相同。 */ var authorization = ACCOUNT_SID + ':' + time; authorization = Base64.encode(authorization); var headers = { 'Accept' :'application/json', 'Content-Type' :'application/json;charset=utf-8', 'Content-Length': JSON.stringify(body).length+'', 'Authorization' : authorization } //4. 发送请求, 并得到返回的结果, 调用callback // callback(true); request({ method : 'POST', url : url, headers : headers, body : body, json : true }, function (error, response, body) { console.log(error, response, body); callback(body.statusCode==='000000'); // callback(true); }); } exports.sendCode = sendCode; /*调用方式 sendCode('13716**2779', randomCode(6), function (success) { console.log(success); })*/
(2)在index.js中调用
var express = require('express'); var router = express.Router(); const md5 = require('blueimp-md5') const models = require('../db/models') const UserModel = models.getModel('user') const _filter = {'pwd': 0, '__v': 0} // 查询时过滤掉 const sms_util = require('../util/sms_util') const users = {} const ajax = require('../api/ajax') var svgCaptcha = require('svg-captcha') /* 发送验证码短信 */ router.get('/sendcode', function (req, res, next) { //1. 获取请求参数数据 var phone = req.query.phone; //2. 处理数据 //生成验证码(6位随机数) var code = sms_util.randomCode(6); //发送给指定的手机号 console.log(`向${phone}发送验证码短信: ${code}`); sms_util.sendCode(phone, code, function (success) {//success表示是否成功 if (success) { users[phone] = code console.log(users[phone]) console.log('保存验证码: ', phone, code) res.send({"code": 0}) } else { //3. 返回响应数据 res.send({"code": 1, msg: '短信验证码发送失败'}) } }) }) /* 短信登陆 */ router.post('/login_sms', function (req, res, next) { var phone = req.body.phone; var code = req.body.code; console.log('/login_sms', phone, code); if (users[code] != code) { res.send({code: 1, msg: '手机号或验证码不正确'}); return; } //删除保存的code delete users[phone]; UserModel.findOne({phone}, function (err, user) { if (user) { req.session.userid = user._id res.send({code: 0, data: user}) } else { //存储数据 const userModel = new UserModel({phone}) userModel.save(function (err, user) { req.session.userid = user._id res.send({code: 0, data: user}) }) } }) })
3、前台实现
(1)输入正确的手机号之后,点击“获取验证码”,会异步调用getCode()方法
a:页面行为
b 调用getCode方法获取验证码
c在methods中定义获取验证码函数
// 异步获取短信验证码 async getCode () { // 如果当前没有计时 if(!this.computeTime) { // 启动倒计时 this.computeTime = 60 this.intervalId = setInterval(() => { this.computeTime-- if(this.computeTime<=0) { // 停止计时 clearInterval(this.intervalId) } }, 1000) // 发送ajax请求(向指定手机号发送验证码短信) const result = await reqSendCode(this.phone) if(result.code===1) { // 显示提示 this.showAlert(result.msg) // 停止计时 if(this.computeTime) { this.computeTime = 0 clearInterval(this.intervalId) this.intervalId = undefined } } } },
d.异步请求时会调用自己封装的请求函数
//6.发送短信验证码 export const reqSendCode = (phone)=>ajax('/api/sendcode', {phone})
import axios from 'axios' export default function ajax(url = '', data = {}, type = 'GET') { return new Promise(function (resolve, reject) { let promise if (type === 'GET') { // 准备url query 参数数据 let dataStr = '' //数据拼接字符串 Object.keys(data).forEach(key => { dataStr += key + '=' + data[key] + '&' }) if (dataStr !== '') { dataStr = dataStr.substring(0, dataStr.lastIndexOf('&')) url = url + '?' + dataStr } // 发送get 请求 promise = axios.get(url) } else { // 发送post 请求 promise = axios.post(url, data) } promise.then(response => { resolve(response.data) }) .catch(error => { reject(error) }) }) }
e.后台接受到get请求,会执行后台index.js中的get请求方式,先生成验证码,并且提示到手机上
router.get('/sendcode', function (req, res, next) { //1. 获取请求参数数据 var phone = req.query.phone; //2. 处理数据 //生成验证码(6位随机数) var code = sms_util.randomCode(6); //发送给指定的手机号 console.log(`向${phone}发送验证码短信: ${code}`); sms_util.sendCode(phone, code, function (success) {//success表示是否成功 if (success) { users[phone] = code console.log(users[phone]) console.log('保存验证码: ', phone, code) res.send({"code": 0}) } else { //3. 返回响应数据 res.send({"code": 1, msg: '短信验证码发送失败'}) } }) })
f.用户收到验证码,并且输入点击“登录”按钮,提交表单,再次对服务器发起post请求,服务器在验证
router.post('/login_sms', function (req, res, next) { var phone = req.body.phone; var code = req.body.code; console.log('/login_sms', phone, code); if (users[code] != code) { res.send({code: 1, msg: '手机号或验证码不正确'}); return; } //删除保存的code delete users[phone]; UserModel.findOne({phone}, function (err, user) { if (user) { req.session.userid = user._id res.send({code: 0, data: user}) } else { //存储数据 const userModel = new UserModel({phone}) userModel.save(function (err, user) { req.session.userid = user._id res.send({code: 0, data: user}) }) } }) })
g.前台接受到后台的响应数据之后,还需要进行以下操作
1)将电话号码保存到vuex的state中去
2)进行路由跳转
if(this.loginWay) { // 短信登陆 const {rightPhone, phone, code} = this if(!this.rightPhone) { // 手机号不正确 this.showAlert('手机号不正确') return } else if(!/^\d{6}$/.test(code)) { // 验证必须是6位数字 this.showAlert('验证必须是6位数字') return } // 发送ajax请求短信登陆 result = await reqSmsLogin(phone, code) } // 停止计时 if(this.computeTime) { this.computeTime = 0 clearInterval(this.intervalId) this.intervalId = undefined } // 根据结果数据处理 if(result.code===0) { const user = result.data cosole.log(user) // 将user保存到vuex的state this.$store.dispatch('recordUser', user) // 去个人中心界面 this.$router.replace('/profile') } else { // 显示新的图片验证码 this.getCaptcha() // 显示警告提示 const msg = result.msg this.showAlert(msg) } }
三、总结