目录
用途说明
组成部分
目录结构
原理时序
云端一体组件介绍
验证码配置(可选):
普通验证码组件
公共模块
云函数公用模块
项目实战
创建云函数
创建注册页
创建云函数
关联公用模块 uni-captcha
刷新验证码
自定义实现 验证码
获取验证码
刷新验证码
校验验证码
下载地址:uni-captcha - DCloud 插件市场
GitCode 仓库:uniCaptcha: 基于uniCloud的验证码模块
用途说明
主要起到人机校验或其他限制调用的作用,如:
- 防止机器冒充人类做暴力破解
- 防止大规模在线注册滥用服务
- 防止滥用在线批量操作
- 防止信息被大量采集聚合
常见的业务场景有:
- 注册环节:防止无效垃圾注册,从源头进行管理
- 登录环节:防止撞库攻击、暴力破解,保障用户数据
- 短信防刷:减少短信接口被刷情况,减少企业不必要成本
- 互动环节:防止批量垃圾互动信息,破坏用户UGC内容生态
- 激励领取:防止被批量褥羊毛
组成部分
- 数据表:opendb-verify-codes,用于存储验证码相关数据
- 公共模块:
uni-captcha
,集成获取、刷新、校验验证码 - 云对象:
uni-captcha-co
,集成获取验证码的api - 云端一体组件:
uni-captcha
和uni-popup-captcha
,集成创建、刷新、显示验证码
目录结构
原理时序
- 客户端,向服务端请求某一应用场景的验证码。提示:这里用场景值
scene
,表示应用场景,用于防止不同功能的验证码混用,如:login
、pay
- 服务端,创建验证码,即:向数据表
opendb-verify-codes
中创建状态为待验证的验证码记录(作废同一个设备id和场景值的旧验证码记录),并返回格式为base64的图形验证码资源数据。提示:这里的数据表,状态字段名:state
用0
表示待验证,用2
表示已作废。 - 客户端,得到验证码图片,用户识别后输入验证码的值与表单数据一起提交至服务端
- 服务端,云函数或clientDB action中校验验证码,决定是否执行业务逻辑。如果验证码错误则返回错误信息,客户端再重复步骤1-3。提示:验证验证码,可以使用封装好的公共模块的
verify
方法详情,也可以直接查库校验。
以上即完整的流程。 如果在前端表单页面中,使用本插件封装好的云端一体组件,并配置组件的属性场景值scene
,即等价于如上步骤1-3;
本插件已集成使用示例,使用HBuilderX导入示例项目体验;另外你也可以参考插件在uni-starter中的应用
云端一体组件介绍
内置调用uni-captcha-co
云对象集成创建/刷新验证码,组件支持双向数据绑定。
验证码配置(可选):
参数说明:
字段 | 类型 | 默认值 | 说明 |
---|---|---|---|
width | Number | 150 | 图片宽度 |
height | Number | 40 | 图片高度 |
background | String | #FFFAE8 | 验证码背景色,设置空字符'' 不使用背景颜色 |
size | Number | 4 | 验证码长度,最多 6 个字符 |
noise | Number | 4 | 验证码干扰线条数 |
color | Boolean | false | 字体是否使用随机颜色,当设置background 后恒为true |
fontSize | Number | 40 | 字体大小 |
ignoreChars | String | 忽略哪些字符 | |
mathExpr | Boolean | false | 是否使用数学表达式 |
mathMin | Number | 1 | 表达式所使用的最小数字 |
mathMax | Number | 9 | 表达式所使用的最大数字 |
mathOperator | String | 表达式所使用的运算符,支持 + 、- 。不传则随机使用 | |
expiresDate | Number | 180 | 验证码过期时间(s) |
scene | Object | 根据场景值配置(版本号:0.6.0+ 支持) |
普通验证码组件
组件名:uni-captcha
组件遵从easycom组件规范
使用示例:
<template>
<uni-captcha scene="场景值" v-model="验证码的值"></uni-captcha>
</template>
Props:
字段 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
scene | String | 是 | - | 使用场景值,用于防止不同功能的验证码混用,如:login 、pay |
value/v-model | String | - | - | 验证码的值 |
公共模块
- 云端一体组件
uni-captcha
和uni-popup-captcha
,已经集成公共模块的获取验证码create
和刷新验证码refresh
接口。 - 引入公共模块请参考云函数公用模块
云函数公用模块
云函数支持公共模块。多个云函数的共享部分,可以抽离为公共模块,然后被多个云函数引用。
版本要求:HBuilderX 2.6.6+
以下面的目录结构为例,介绍一下如何使用。
新建并引入公用模块
- 在
cloudfunctions
目录下创建common
目录 - 在
common
目录右键创建公用模块目录(本例中为hello-common
,见下方示例图),会自动创建入口index.js
文件和package.json
,不要修改此package.json的name字段 - 在
hello-common
右键上传公用模块 - 在云函数上右键选择
管理公共模块依赖
,添加依赖的公共模块
公共模块依赖其他公共模块同理
注意事项
- 如果要更新所有依赖某公用模块的云函数,可以在
common
目录下的公用模块目录(本例中为hello-common
)右键选择更新依赖本模块的云函数
- 公用模块命名不可与nodejs内置模块重名
- 从插件市场导入或者其他地方复制项目可能会导致
npm install
创建的软链接失效,如果遇到这种情况请删除node_modules
和package-lock.json
重新npm install
使用公用模块
仍以上面的目录为例,在公用模块内exports
,在云函数内require
即可。示例代码如下:
// common/hello-common/index.js
function getVersion() {
return '0.0.1'
}
module.exports = {
getVersion,
secret: 'your secret'
}
// use-common/index.js
'use strict';
const {
secret,
getVersion
} = require('hello-common')
exports.main = async (event, context) => {
let version = getVersion()
return {
secret,
version
}
}
项目实战
创建云函数
我们来创建uni-captcha云函数,如下图右击cloudfunctions,选择新建云函数/云对象。
弹出如下图后,我们选择uni-captcha即可,点击确认。 、
然后cloudfunctions中,则会生成common/uni-captcha和uni-captcha-co两个模块。
创建注册页
云函数都创建成功后,我们实现一个简单的登录页面,如下图:
博主 from表单使用了 uview 可以换成 uni-ui
uView - 多平台快速开发的UI框架 - uni-app UI框架
<template>
<view class="container">
<view class="wrapper">
<view class="title">
用户注册
</view>
<view class="input-content">
<u-form :model="form" ref="uForm">
<u-form-item label="手机" prop="Phone">
<u-input placeholder="请输入手机号" v-model="form.Phone" />
</u-form-item>
<u-form-item label="密码" prop="Password">
<u-input type="password" placeholder="请输入密码" v-model="form.Password" />
</u-form-item>
<u-form-item label="密码" prop="Password1">
<u-input type="password" placeholder="确认密码" v-model="form.Password1" />
</u-form-item>
<u-form-item prop="captcha" label="验证码" label-width="100rpx">
<uni-captcha :scene="form.scene" v-model="form.captcha"></uni-captcha>
</u-form-item>
</u-form>
</view>
<button class="confirm-btn" @click="register()">注册</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 校验规则
rules: {
Phone: [{
required: true,
message: '请输入手机号',
// 可以单个或者同时写两个触发验证方式
trigger: ['change', 'blur'],
},
{
// 自定义验证函数,见上说明
validator: (rule, value, callback) => {
// 上面有说,返回true表示校验通过,返回false表示不通过
// this.$u.test.mobile()就是返回true或者false的
return this.$u.test.mobile(value);
},
message: '手机号码不正确',
// 触发器可以同时用blur和change
trigger: ['change', 'blur'],
}
],
Password: [{
required: true,
message: '请输入密码',
// 可以单个或者同时写两个触发验证方式
trigger: ['change', 'blur'],
},
{
min: 6,
message: '密码不能少于5个字',
trigger: 'change'
}
],
Password1: [{
required: true,
message: '请输入确认密码',
// 可以单个或者同时写两个触发验证方式
trigger: ['change', 'blur'],
},
{
// 自定义验证函数,见上说明
validator: (rule, value, callback) => {
// 上面有说,返回true表示校验通过,返回false表示不通过
// this.$u.test.mobile()就是返回true或者false的
return value === this.form.Password
},
message: '两次密码不一致',
// 触发器可以同时用blur和change
trigger: ['change', 'blur'],
}
],
captcha: [{
required: true,
message: '请输入验证码',
// 可以单个或者同时写两个触发验证方式
trigger: ['change', 'blur'],
}]
},
form: {
Phone: '',
Password: '',
Password1: '',
scene: "register",
captcha: ""
},
}
},
// 必须要在onReady生命周期,因为onLoad生命周期组件可能尚未创建完毕
onReady() {
console.log("执行");
this.$refs.uForm.setRules(this.rules);
},
methods: {
register() {
this.$refs.uForm.validate(valid => {
if (valid) {
let data = {
...this.form
}
uniCloud.callFunction({
name: "register",
data: data
}).then(res => {
console.log(res);
if (res.result.code == 0) {
this.$u.toast('注册成功');
setTimeout(() => {
uni.navigateBack()
}, 300)
} else {
this.form['scene'] = 'register' + Math.random();
this.$u.toast(res.result.message);
}
})
} else {
console.log('验证失败');
}
});
}
},
}
</script>
/* <style lang='scss'>
.container {
padding-top: 50px;
width: 100vw;
height: 100vh;
background: #fff;
}
.wrapper {
background: #fff;
padding-bottom: 40upx;
}
.title {
text-align: center;
margin-bottom: 100rpx;
font-size: 46upx;
color: #555;
text-shadow: 1px 0px 1px rgba(0, 0, 0, .3);
}
.input-content {
padding: 0 60upx;
}
.input-item {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
padding: 0 30upx;
background: $page-color-light;
height: 120upx;
border-radius: 4px;
margin-bottom: 50upx;
&:last-child {
margin-bottom: 0;
}
.tit {
height: 50upx;
line-height: 56upx;
font-size: $font-sm+2upx;
color: $font-color-base;
}
input {
height: 60upx;
font-size: $font-base + 2upx;
color: $font-color-dark;
width: 100%;
}
}
.confirm-btn {
width: 630upx;
height: 76upx;
line-height: 76upx;
border-radius: 50px;
margin-top: 70upx;
background: $uni-theme-color;
color: #fff;
font-size: $font-lg;
&:after {
border-radius: 100px;
}
}
.forget-section {
font-size: $font-sm+2upx;
color: $uni-theme-color;
text-align: center;
margin-top: 40upx;
}
.captcha_box {
.input {}
.captcha {
width: 240rpx;
height: 72rpx;
}
}
</style>
创建云函数
此时我们创建一个云函数,用于对表单中输入的验证码,进行校验其是否正确。还是在cloudfunctions上右击,选择“新建云函数/云对象”,如下图:
点击创建后,cloudfunctions中会生成云函数。如下图:
此时可以在index.js中添加图形验证码校验功能,返回校验结果。代码如下:
'use strict';
//导入验证码公共模块
const uniCaptcha = require('uni-captcha')
const db = uniCloud.database();
exports.main = async (event) => {
const { Phone, Password, scene, captcha } = event;
try {
// 校验验证码
let res = await uniCaptcha.verify({ scene, captcha })
// 验证通过
if (res.code == 0) {
// 校验是否已经注册
const userExists = await db.collection("User").where({ Phone: Phone }).get();
if (userExists.data.length !== 0) {
return {
code: 1,
message: "该账号已注册",
};
}
// 添加用户
await db.collection("User").add({
time: Date.now(),
Phone: Phone,
Password: Password,
});
return {
code: 0,
message: "注册成功"
};
} else {
// 验证失败
return {
code: -1,
message: res.errMsg || res.message || "验证码异常",
};
}
} catch (error) {
// 出现异常
console.error('添加用户失败:', error);
return {
code: -1,
message: "注册失败,请稍后重试",
};
}
};
关联公用模块 uni-captcha
在云函数上鼠标右击,选择”管理公共模块或扩展库依赖“
选择”uni-captcha“公共模板,点击确认。
刷新验证码
另外,我们发现如果验证码错误后,显示的验证码不会自动刷新。由于这里我们使用的是uni-app的扩展UI组件,功能不好升级维护,如果觉得此组件不好用,也可以自己使用uni-captcha-co获取验证进行个性化操作。
这里主要是为了演示,就先在原基本上完成刷新功能。打开uni_modules目录,找到uni-captcha组件,再打开components目录中的uni-captcha,我们来看下内部是如何实现的。
如上图所示,我们发现应用场景发生改变后,验证码会重新获取。所以上文中这样做的
加入随机数让其变化
自定义实现 验证码
获取验证码
用于新的验证码记录(使用云端一体组件的用户可以忽略)
//引入公共模块
const uniCaptcha = require('uni-captcha')
module.exports = {
async createCaptcha({scene}) {
return await uniCaptcha.create({
scene,
width:100,
height:44
});
}
}
参数说明
字段 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
scene | String | 是 | - | 使用场景值,用于防止不同功能的验证码混用,如:login 、pay |
deviceId | String | - | - | 设备 id,如果不传,将自动从 uniCloud 上下文获取 |
uniPlatform | String | - | - | uni-app 运行平台 |
width | Number | - | 150 | 图片宽度 |
height | Number | - | 40 | 图片高度 |
background | String | - | #FFFAE8 | 验证码背景色,设置空字符'' 不使用背景颜色 |
size | Number | - | 4 | 验证码长度,最多 6 个字符 |
noise | Number | - | 4 | 验证码干扰线条数 |
color | Boolean | - | false | 字体是否使用随机颜色,当设置background 后恒为true |
fontSize | Number | - | 40 | 字体大小 |
ignoreChars | String | - | '' | 忽略哪些字符 |
mathExpr | Boolean | - | false | 是否使用数学表达式 |
mathMin | Number | - | 1 | 表达式所使用的最小数字 |
mathMax | Number | - | 9 | 表达式所使用的最大数字 |
mathOperator | String | - | '' | 表达式所使用的运算符,支持 + 、- 。不传则随机使用 |
expiresDate | Number | - | 180 | 验证码过期时间(s) |
注意:
- 自
uni-captcha 0.3.0
起,支持在unicloud配置中心uni-config-center
->uni-captcha
->config.json
中配置参数默认值 - 如果想替换字体,请保证字体格式为
.ttf
且包含ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-
字符
响应参数
字段 | 类型 | 说明 |
---|---|---|
errCode | Number | 错误码,0 表示成功 |
errMsg | String | 详细信息 |
captchaBase64 | String | 验证码:base64 格式 |
刷新验证码
作废相同设备id和场景值的验证码记录,并创建新的验证码记录(使用云端一体组件的用户可以忽略)
//引入公共模块
const uniCaptcha = require('uni-captcha')
const db = uniCloud.database();
const verifyCodes = db.collection('opendb-verify-codes')
module.exports = {
async refreshCaptcha({scene}) {
let res = await verifyCodes.where({scene,deviceId,state:0}).limit(1).get()
if(res.data.length){
return await uniCaptcha.refresh({
scene,
width:100,
height:44
});
}else{
return {
errCode: "uni-captcha-refresh-fail",
errMsg: '未找到相同设备id和场景值的有效验证码记录'
}
}
}
}
参数说明
字段 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
scene | String | 是 | - | 类型,用于防止不同功能的验证码混用 |
deviceId | String | - | - | 设备 id,如果不传,将自动从 uniCloud 上下文获取 |
响应参数
字段 | 类型 | 说明 |
---|---|---|
errCode | Number | 错误码,0 表示成功 |
errMsg | String | 详细信息 |
captchaBase64 | String | 验证码:base64 格式 |
注意:
- 支持传入 create 方法的所有参数,如果不传,则自动按照 deviceId 匹配上次生成时的配置生成新的验证码
校验验证码
用于验证用户输入的验证码是否正确
const uniCaptcha = require('uni-captcha')
module.exports = {
async verify({scene,captcha}) {
let res = await uniCaptcha.verify({scene,captcha})
if(res.code == 0){
//...这里写你的业务逻辑
}else{
return res
}
}
}
参数说明
字段 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
scene | String | 是 | - | 类型,用于防止不同功能的验证码混用 |
captcha | String | 是 | - | 验证码 |
deviceId | String | - | - | 设备 id,如果不传,将自动从 uniCloud 上下文获取 |
响应参数
字段 | 类型 | 说明 |
---|---|---|
errCode | Number | 错误码,0 表示成功 |
errMsg | String | 详细信息 |
注意:
- 若提示验证码失效,请重新获取
- 如果为了更小的代码体积,不想使用本方法,也可以直接查库校验