一、前言
1、动态获取图片验证码
2、实现手机验证码登录(工具准备)
3、手机验证码登录(后台实现)
3、前台实现
二、主要内容
1、动态获取图片验证码
(1)请求的接口如下,返回的是一张svg的图片
## 获取一次性验证码
### 请求URL:
http://localhost:3000/captcha
### 请求方式:
(2)初次显示图片,可以直接在image中的src中请求路径直接得到
<!--第一次显示直接请求http://localhost:4000下面的-->
<!--点击图片的时候要更新图片,注册一个点击事件-->
<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)
})
})
}
ajax函数封装
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)
}
}
三、总结
虽然现在走得很慢,但不会一直这么慢