背景
最近开源了一款面向中后台的低代码系统,热度持续高涨,微信群已近百人,PV
已突破1w
次。很多用户反馈,为啥没有注册功能,我想自己注册一个账号去体验,不想共用demo
账号。
于是本周就开始思考如何实现一个注册功能。注册是用 微信扫码登录、手机号登录还是邮箱登录?
本文主要把我的思考路径和最终方案整理给大家,仅供参考。同时这两周也持续迭代了marsview
系统,提供了很多有趣的功能,欢迎大家文章末尾体验。
效果展示
方案选择
微信扫码
微信扫码登录自然是体验最好的方式,然后我作为一个个人开发者,不具备一些条件。因为微信扫码需要企业主体,同时需要开通微信认证,这些都比较耗时以及有一定的门槛。
我想后续我会逐步考虑,但当下不是最好的选择。
手机号登录
手机号登录虽然可以保存高质量的用户群体,但是也会存在一些问题,比如:用户注册意愿?短信如何防刷?购买短信成本等。
一般很多用户面对一个新网站,用手机号注册意愿不高,因为担心手机号泄露和骚扰,所以我自己主观感觉,手机号注册意愿很低。
其次短信防刷,目前行业倒是有一些方案可供参考,比如:
- 调用短信接口前,需要先做滑块验证,通过以后才能调用短信接口。
插件:rc-slider-captcha 或者 react-slider-vertify
图片参考:picsum.photos/ 这个网站可以得到很多用作验证的背景图片。
- 接口做IP次数验证,每个IP每天限制5次。
- 接口做origin限制。
- 前后端增加签名验证。使用对称加密对参数、时间戳、随机数等加密,生产签名,传给后端做验证,通过后,方可调用。
这些方案,我觉得都可行,但目前我的低代码系统主打开源,暂时还不想增加额外的购买成本,所以手机号的方案,暂时不做,等后续平台稳定以后,再考虑接入手机号。
邮箱注册
邮箱注册应该算是当前最经济实惠的一个方案了,几乎没有成本,而且用户使用邮箱注册的意愿要比手机号更强烈,因为手机号担心泄露和骚扰问题,邮箱相对而言好很多。
邮箱注册实现
前端实现
1. 静态代码如下:
<div className={style.form}>
<div className={style.title}>账号注册</div>
<Form
name="basic"
layout="vertical"
className={style.form}
onFinish={onFinish}
size="large"
form={form}
>
<Form.Item<FieldType>
name="userName"
rules={[
{ required: true, message: '请输入邮箱' },
{ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: '请输入正确的邮箱' },
]}
>
<Input prefix={<UserOutlined />} placeholder="请输入个人邮箱" />
</Form.Item>
<Form.Item>
<Space>
<Form.Item<FieldType> name="code" noStyle rules={[{ required: true, message: '请输入验证码' }]}>
<InputNumber prefix={<SafetyOutlined />} style={{ width: '100%' }} placeholder="验证码" />
</Form.Item>
<Button type="primary" onClick={handleCreateCode} disabled={count > 0} loading={loading1}>
{count > 0 ? count + 's' : '获取验证码'}
</Button>
</Space>
</Form.Item>
<Form.Item<FieldType> style={{ marginTop: 32 }} name="userPwd" rules={[{ required: true, message: '请输入密码' }]}>
<Input.Password prefix={<LockOutlined />} />
</Form.Item>
<Form.Item style={{ marginTop: 40 }}>
<Button type="primary" block htmlType="submit" loading={loading2}>
登录
</Button>
</Form.Item>
<Form.Item style={{ marginTop: 40 }}>
<div style={{ textAlign: 'center' }}>
'已有账号?去登录'
</div>
</Form.Item>
</Form>
</div>
2. 实现一个倒计时
export default function Login() {
const [count, setCount] = useState(0);
// 生成验证码
const handleCreateCode = () => {
// 调用验证码接口
....
setCount(60);
...
}
useEffect(() => {
const timer = setTimeout(() => {
if (count > 0) {
setCount(count - 1);
}
}, 1000);
// 清理函数
return () => clearTimeout(timer);
}, [count]);
return <>...</>
}
点击获取验证码按钮时,调用接口,初始化count
值,通过setTimeout
触发倒计时。
3. 按钮在倒计时期间,增加状态判断。
<Button
type="primary"
onClick={handleCreateCode}
disabled={count > 0}
loading={loading1}
>
{count > 0 ? count + 's' : '获取验证码'}
</Button>
- 增加
Loading
状态。 - 如果倒计时结束,需要启用按钮,否则禁用。
- 按钮显示倒计时的数字。
接口实现
1. 安装nodemailer
插件
yarn add nodemailer
2. 使用Koa
实现邮箱服务完整配置代码
const nodemailer = require("nodemailer");
const { Keyv } = require("keyv");
const keyv = new Keyv();
/**
* 用户注册
*/
router.post("/sendEmail", async (ctx) => {
try {
const { email } = ctx.request.body;
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
util.fail(ctx, "邮箱不能为空或格式错误");
return;
}
const val = await keyv.get(email);
if (val) {
util.fail(ctx, "验证码已发送,请查收");
return;
}
let transporter = nodemailer.createTransport({
host: "smtp.163.com",
port: 465,
auth: {
user: "xxx@163.com", // 你的163邮箱地址
pass: "xxx", // 你的邮箱密码
},
});
const random = Math.random().toString().slice(2, 7);
let mailOptions = {
from: '"Marsview" <xxx@163.com>', // 发送者地址
to: email, // 接收者列表
subject: "Marsview账号注册", // 主题行
text: "验证码发送", // 纯文字正文
html: `您当前的验证码为:<b>${random}</b>,3分钟内有效。感谢您成为Marsview一员。`, // HTML正文
};
await transporter.sendMail(mailOptions);
await keyv.set(email, random, 3 * 60 * 1000);
util.success(ctx, "发送成功");
} catch (error) {
util.fail(ctx, "发送失败,请重试");
}
});
nodemailer
插件支持很多格式的邮箱,比如:qq
、163
、gmail
等,通过这个插件几乎没什么难度就快速实现邮件发送功能。
3. 有效期控制。
这里面有几个问题需要跟大家分享,邮箱发送的验证码有效期怎么控制?大家可能会说redis
,的确如此,但是作为个人开发者,难不成再去购买一台redis
服务器,那不可能,所以,我们只能选择保存在内存中,占用服务器资源。
- 安装插件
yarn add keyv
keyv
插件是一款键值对保存的插件,它可以对接redis
、mysql
、mongo
等等,每天下载量3000
多万,非常受欢迎,同时它默认是保存在内存中,对于有咱们这种穷鬼来说,再适合不过了。
- 发送前验证
const { Keyv } = require("keyv");
const keyv = new Keyv();
...
const val = await keyv.get(email);
if (val) {
util.fail(ctx, "验证码已发送,请查收");
return;
}
发送之前,先通过keyv.get
判断邮箱是否存在,如果存在,说明验证码还没过期,就不要重复发送了。
- 保存验证码
// 随机生成一个5位验证码
const random = Math.random().toString().slice(2, 7);
// 调用发送API
await transporter.sendMail(mailOptions);
// 保存验证码到缓存中,有效期3分钟
await keyv.set(email, random, 3 * 60 * 1000);
注意:keyv.set 是一个 Promise函数
总结
我就是借用了nodemailer
和keyv
插件实现了免费的邮件发送和有效期控制的功能,当然在不同阶段,可能会选择不同的方案,或许当下免费的是最适合我的。
欢迎大家体验低代码系统,加入微信群一起沟通交流。这两周,我们又迭代了很多有趣的功能,比如:
- 优化首页加载
- 增加个人是市场页面区分。
- 自定义组件引入
AI
助手,自动生成代码。 - 开发抽屉组件、结果组件等。
- 增加使用文档,录制了一些教学视频(事件流如何使用、综合案例等)。
欢迎在线体验:www.marsview.cc/
文档地址:docs.marsview.cc/
低代码效果图: