提示:node发送手机验证码,koa接入短信demo(云信通)
前言
node发送手机验证码,koa接入短信demo(云信通)
一、云信通配置
1、云信通注册登陆地址:
https://www.yuntongxun.com/user/login
2、账号信息,接入参数ACCOUNT SID、AUTH TOKEN、App ID
3、短信接入文档
https://doc.yuntongxun.com/pe/5a533de33b8496dd00dce07c
4、配置参数
(1)拼接请求地址
(2)SigParameter规则
(3)请求头部以及Authorization规则
(4)请求数据data,模板短信
二、使用步骤
1.vue中Login.vue
代码如下(示例):
<template>
<div class="login_box">
<div class="login_list">
<el-form
:model="user"
status-icon
:rules="rules"
ref="loginForm"
label-width="80px"
class="login_form"
>
<el-form-item v-if="loginType='regist'" label="电话" prop="phone" class="login_list_item">
<el-input v-model.number="user.phone"></el-input>
</el-form-item>
<el-form-item v-if="loginType='regist'" label="验证码" prop="code" class="login_list_item">
<div class="login_item">
<el-input v-model.number="user.code"></el-input>
<el-button type="primary" @click="getPhoneCode" round>获取验证码</el-button>
</div>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { getPhoneCode } from "@/axios/index";
export default {
name: "Login",
data() {
var validatePhone = (rule, value, callback) => {
let reg=/^1[34578]\d{9}$/;
if (value === "") {
callback(new Error("请输入电话"));
} else if (!reg.test(this.user.phone)) {
callback(new Error("请输入正确的电话号码"));
} else {
callback();
}
};
var validateCode = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入验证码"));
} else {
callback();
}
};
return {
imgUrl: null,
msg: "用户注册",
loginType: "regist",
user: {
phone: "",
code: "",
},
rules: {
phone: [{required: false},{ validator: validatePhone, trigger: "blur" }],
code: [{required: false},{ validator: validateCode, trigger: "blur" }],
},
};
},
methods: {
async getPhoneCode(){
if(!this.user.phone||this.user.phone==''){
this.$message({
type:'error',
message: "请填写正确电话号码"
});
return;
}
const obj = await getPhoneCode({phone:this.user.phone});
console.log(obj,"++++++++++++")
}
},
};
</script>
<style scoped lang="scss">
.login_box {
position: relative;
height: 100%;
.login_list {
width: 400px;
position: absolute;
box-sizing: border-box;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
.login_title {
font-size: 16px;
text-align: center;
}
.login_item{
display: flex;
.el-button.is-round{
padding: 0 14px;
margin-left: 30px;
height: 26px;
position: relative;
top: 8px;
}
}
}
}
</style>
<style>
.login_box .login_list .el-form-item__label{
text-align: justify;
height: 40px;
overflow: hidden;
}
.login_box .login_list .el-form-item__label::after{
content: "";
display: inline-block;
width: 100%;
height: 100%;
}
</style>
2.vue中axios.js
代码如下(示例):
import axios from 'axios';
import { Loading, Message } from 'element-ui';
let urlData = { basicUrl: "http://127.0.0.1:3002" }
let loading;
const instance = axios.create({
baseURL: urlData.basicUrl,
timeout: 1000,
headers: { "X-Requested-With": "XMLHttpRequest" },
withCredentials: false,
});
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
loading = Loading.service({
lock: true, // 是否锁屏
text: '正在加载...', // 加载动画的文字
spinner: 'el-icon-loading', // 引入的loading图标
background: 'rgba(0, 0, 0, 0.3)', // 背景颜色
})
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
loading.close();
// 对响应数据做点什么
return response.data;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export const getPhoneCode = async (data) => {
return instance.post('/getPhoneCode', data);
}
export default instance;
3.koa中routes.js
代码如下(示例):
import Router from 'koa-router';
import fs, { readFileSync } from 'fs';
import { phoneCode } from './phoneCode.js'
const router = new Router();
const getPhoneCode = async ctx =>{
const data = await phoneCode(ctx.request.body.phone);
console.log(data)
ctx.body = {
code:200,
data:data,
msg:'success'
}
}
router.post('/getPhoneCode ',getPhoneCode );
export default router;
4.koa中phoneCode.js
代码如下(示例):
import md5 from 'md5';
import axios from 'axios';
import { Base64 } from 'js-base64';
const getTimeString = ()=>{
let date = new Date();
// date.getMonth()获取当前月份(0-11,0代表1月) 需要+1
let timeArr = [ date.getFullYear(),date.getMonth()+1,date.getDate(),date.getHours(),date.getMinutes(),date.getSeconds() ]
let timeString = '';
timeArr.forEach(num=>{ timeString+=num>9?''+num:'0'+num; });
return timeString;
}
export const phoneCode = async (phone)=> {
/*
* 业务URL格式:/2013-12-26/Accounts/{accountSid}/SMS/{funcdes}?sig={SigParameter}
* 在URL格式中 {}内的内容表示为参数,非{}的内容固定不变。
*/
let ACCOUNT_SID = '8aaf0708802d0d8501804ac40bef0768';
let AUTH_TOKEN = '9a383fdf39a84084891443c63393e410';
let App_ID = '8aaf0708802d0d8501804ac40cec076f';
/*
* accountSid String 必选 开发者主账户ACCOUNT SID(登陆官网在管理控制台获取)
* SigParameter String 必选 REST API 验证参数,生成规则如下1.使用MD5加密(账户Id + 账户授权令牌 + 时间戳)。其中账户Id和账户授权令牌根据url的验证级别对应主账户。时间戳是当前系统时间,格式'yyyyMMddHHmmss'。时间戳有效时间为24小时,如:201404161420302.SigParameter参数需要大写,如不能写成sig=abcdefg而应该写成sig=ABCDEFG
*/
let timeString = getTimeString();
let sig = md5(ACCOUNT_SID+AUTH_TOKEN+timeString).toUpperCase(); //这里不大写也可以
let url = 'https://app.cloopen.com:8883/2013-12-26/Accounts/'+ACCOUNT_SID+'/SMS/TemplateSMS?sig='+sig;
/*
* Accept String 必选 客户端响应接收数据格式:application/xml、application/json
* Content-Type String 必选 类型:application/xml;charset=utf-8、application/json;charset=utf-8
* Content-Length String 必选 Content-Length
* Authorization String 必选 验证信息,生成规则详见下方说明1.使用Base64编码(账户Id + 冒号 + 时间戳)其中账户Id根据url的验证级别对应主账户2.冒号为英文冒号3.时间戳是当前系统时间,格式'yyyyMMddHHmmss',需与SigParameter中时间戳相同。
*/
let Authorization = Base64.encode(ACCOUNT_SID+':'+timeString);
let headers = {
'Accept':'application/json',
'Content-Type':'application/json;charset=utf-8',
// 'Content-Length':JSON.stringify(data).length+'', //这里可以不传,传必须是请求数据stringify的length
'Authorization':Authorization
}
/*
* to String 必选 短信接收端手机号码集合,用英文逗号分开,每批发送的手机号数量不得超过200个
* appId String 必选 应用Id,官网控制台应用列表获取
* templateId String 必选 模板Id,官网控制台模板列表获取。测试模板id是1。测试模板的内容是:【云通讯】您使用的是云通讯短信模板,您的验证码是{1},请于{2}分钟内正确输入
* datas Array 可选 内容数据外层数组节点
* data String 可选 内容数据,用于替换模板中{序号},模板如果没有变量,此参数可不传,多个变量,使用数组的数据格式
* subAppend String 可选 扩展码,四位数字 0~9999
* reqId String 可选 第三方自定义消息id,最大支持32位,同账号下同一自然天内不允许重复。
*/
//生成验证码
let codeStr = '';
for(let i=0;i<6;i++){
codeStr += Math.floor(Math.random()*10);
}
let data = {
to:phone,
appId:App_ID,
templateId:'1',
datas:[codeStr,'1']
}
let resData = {codeStr}
await axios({
headers,
method:'POST',
url,
data
}).then(res=>{
resData.data = res.data
console.log(res.data,'+++++++++++++')
}).catch(err=>{
console.log(err,'err')
});
return resData;
}
5.koa中app.js
代码如下(示例):
import koa from 'koa';
import cors from 'koa-cors';
import router from './routes/routes.js';
import staticFiles from 'koa-static';
import koaBody from 'koa-body';
import session from 'koa-session'
import path from 'path';
const __dirname = path.resolve();
const app = new koa();
app.use(cors({ // 指定一个或多个可以跨域的域名
origin: function (ctx) { // 设置允许来自指定域名请求
if (ctx.url === '/') {
return "*"; // 允许来自所有域名请求, 这个不管用
}
// return 'http://localhost:8000'; // 这样就能只允许 http://localhost:8000 这个域名的请求了
return '*'; // 这样就能只允许 http://localhost:8000 这个域名的请求了
},
maxAge: 5, // 指定本次预检请求的有效期,单位为秒。
credentials: true, // 是否允许发送Cookie
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 设置所允许的HTTP请求方法
allowHeaders: ['Content-Type', 'Authorization', 'Accept'], // 设置服务器支持的所有头信息字段
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] // 设置获取其他自定义字段
}))
const sessionConfig = {
key: 'koa:sess', // cookie的key,默认koa:sess
maxAge: 1000*5, // 过期时间,毫秒ms
autoCommit: true, // 提交到响应头
overwrite: true, // 重写
httpOnly: true, // 无法获得Cookie信息
signed: true, // 签名
rolling: true, // 每次刷新
renew: false, // 快过期刷新
};
app.keys = ["long long age"]; // signed签名key
app.use(session(sessionConfig, app)); //第二个参数是app ----------------
//解析formdata过来的数据
app.use(koaBody({
multipart: true,
formidable: {
//上传文件存储目录
uploadDir: path.join(__dirname, `/public/uploads/`),
//允许保留后缀名
keepExtensions: true,
multipart: true,
},
jsonLimit:'10mb',
formLimit:'10mb',
textLimit:'10mb'
}));
app.use(router.routes());
app.use(router.allowedMethods());
app.use(staticFiles(__dirname + '/public'));
app.listen('3002');
console.log("项目启动,访问:","localhost:3002");
总结
踩坑路漫漫长@~@