当前位置: 首页 > news >正文

用户注册这样玩,保你平安

前言

基本上每个系统系统都包含用户注册、发送验证码等基本操作。在前些年,我还记得我在逛 csdn、贴吧、网易新闻等网站的时候是可以不登陆也能浏览完网页内容的,但是近几年这些网站已经改成了不登陆不让用,浏览网页时不时提醒你要进行登录,对于一些不喜欢注册的用户造成了相当大的困扰。

但是不知道大家有没有想过这里面的深层逻辑,就是为什么前些年什么 csdn、贴吧、网易新闻等明明不进行登录浏览网页体验还行,现在要改成这样子?

这里面涉及的因素有很多,比如互联网发展到头、变现困难、存量环境加剧内卷等。

当公司盈利压力变大,老板眼看收益日趋降低,便开始拉领导开会,领导开完会开始 PUA 员工,一层一层递进,辅以绩效、okr 等工具制定目标结果。于是公司底层员工的想法从努力赚钱、升职加薪变成保住饭碗、养活一家老小,对于业务上的月度、季度营收要求自然是各种促进用户付费的手段应上齐上。

这里面提升付费有一个非常重要的前提就是用户,只要有了用户就有付费希望。

如果用户不注册,不留下手机号、邮箱等个人信息,互联网运营又怎么给这些用户发送营销短信和邮件。所以说强制注册本质上是为了公司利益。

只要把用户留下来,留在自己的 APP 里,收集用户信息,后续各种运营活动、支付弹窗、短信找回、活动抽奖一起上,何愁没有用户 😜。

用户信息记录的意义是为了聚集 C 端用户、收集信息,为后续运营活动(提升付费)做准备。就拿淘宝举例,个性化推荐、千人千面、双 11 活动等,这一系列运营活动说到底都是为了提升淘宝的付费金额,提升淘宝平台的 GMV。什么个性化推荐、千人千面说白了就是收集你的个人信息,你的商品点击、浏览、下单等操作都会被淘宝采集,进而通过算法模型进行商品推荐,选出你可能感兴趣的商品展示,从而提升淘宝付费金额。

OK,到这里题外话说多了,虽然说用户注册是一个很基本的逻辑,但是很多人一不小心就会掉坑里。这里我给大家介绍下 waynboot-mall 项目中用户注册是怎么玩的,为什么说可以保你平安。

waynboot-mall 项目是由我开源的一套 H5 商城项目,包含运营后台、H5 商城前台和服务端接口。实现了商城所需的首页展示、商品分类、商品详情、商品 sku、分词搜索、购物车、结算下单、支付宝/微信支付、收单评论以及完善的后台管理等一系列功能。 技术上基于最新得 Springboot3.0 框架开发而来,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等常用中间件。商城模块划分合理、代码质量较高、易于部署,非常适合大家拿来学习使用。

github 地址:https://github.com/wayn111/waynboot-mall

用户注册

在 waynboot-mall 项目中,商城注册页面截图如下。

/captcha 生成图形验证码接口

@ResponseBody
@RequestMapping("/captcha")
public R captcha() {// 1. 创建验证码对象,定义验证码图形的长、宽、以及字数SpecCaptcha specCaptcha = new SpecCaptcha(80, 32, 4);// 2. 生成验证码String verCode = specCaptcha.text().toLowerCase();// 3. 生成验证码唯一keyString captchaKey = IdUtil.getUid();// 4. 存入redis并设置过期时间为30分钟redisCache.setCacheObject(captchaKey, verCode, SysConstants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);// 5. 将key和base64返回给前端return R.success().add("captchaKey", captchaKey).add("image", specCaptcha.toBase64());
}

验证码接口基本是每个系统都有的接口,验证码主要是为了防止别人直接调用接口进行注册操作,是一个安全措施。现在市面上流行的有图形验证码、滑块验证码、点选验证码等,waynboot-mall 项目中使用的图形验证码,大家有兴趣可以了解 tianai-captcha 这个项目,包含滑块验证码、点选验证码等。现在我们对验证码接口进行讲解,

  • 第一步,创建验证码对象,定义验证码图形的长、宽、以及字数(这里创建的 SpecCaptcha 对象来自 easy-captcha 项目)

  • 第二步,生成验证码 verCode

  • 第三步,为验证码生成唯一 captchaKey

  • 第四步,将 captchaKey 作为 key, verCode 作为 value,存入 redis 并设置过期时间

  • 第五步,将 captchaKey 以及验证码图像的 base64 编码返回给前端

前端在调用完 /captcha 接口后,会拿到 captchaKey 以及验证码图像的 base64 编码,之后前端就可以将 base64 编码作为 img 标签 src 属性用作图形验证码展示。

用户输入邮箱和图形验证码后就可以点击发送邮箱验证码了。

调用发送邮箱验证码接口时会将 captchaKey、验证码、手机号等信息一起传给服务端。

/sendEmailCode 发送邮箱验证码接口

@PostMapping("/sendEmailCode")
public R sendEmailCode(@RequestBody RegistryObj registryObj) {String captchaKey = registryObj.getCaptchaKey();String captchaCode = registryObj.getCaptchaCode();String mobile = registryObj.getMobile();if (StringUtils.isBlank(captchaKey)) {return R.error(CUSTOM_ERROR.setMsg("图形验证码错误"));}if (StringUtils.isBlank(captchaCode)) {return R.error(CUSTOM_ERROR.setMsg("图形验证码为空"));}if (StringUtils.isBlank(mobile)) {return R.error(CUSTOM_ERROR.setMsg("手机号为空"));}String redisCode = redisCache.getCacheObject(captchaKey);// 判断验证码codeif (!redisCode.equals(captchaCode.trim().toLowerCase())) {return R.error(USER_CAPTCHA_CODE_ERROR);}// 验证手机号是否唯一long count = iMemberService.count(Wrappers.lambdaQuery(Member.class).eq(Member::getMobile, mobile));if (count > 0) {return R.error(USER_PHONE_HAS_REGISTER_ERROR);}// 生成邮箱验证码codeString emailCode = RandomUtil.randomString(6);// 生成邮箱验证码唯一keyString emailKey = RedisKeyEnum.EMAIL_KEY_CACHE.getKey(IdUtil.getUid());// 存入redis并设置过期时间为20分钟redisCache.setCacheObject(emailKey, emailCode + "_" + mobile,  RedisKeyEnum.EMAIL_KEY_CACHE.getExpireSecond());commonThreadPoolTaskExecutor.execute(() -> {EmailConfig emailConfig = mailConfigService.getById(1L);SendMailVO sendMailVO = new SendMailVO();sendMailVO.setSubject("mall商城注册通知");sendMailVO.setContent("邮箱验证码:" + emailCode);sendMailVO.setTos(Collections.singletonList(registryObj.getEmail()));MailUtil.sendMail(emailConfig, sendMailVO, false, false);});return R.success().add("emailKey", emailKey);
}

一般商城系统中,发送邮箱验证码、短信验证码时都需要进行验证码输入这一步骤,这是为了防止别人直接通过接口调用的形式,浪费我们系统的资源,特别是发送手机验证码、邮件这种资源。发送邮箱验证码接口讲解如下,

  • 第一步,校验 captchaKey、captchaCode、mobile 必传参数

  • 第二步,根据 captchaKey 读取 redis 中存放的验证码 code,与用户输入 captchaCode 进行比较

  • 第三步,验证用户手机号是否唯一

  • 第四步,生成六位邮箱验证码 emailCode

  • 第五步,生成邮箱验证码唯一 emailKey

  • 第六步,将 emailKey 作为 key, emailCode_mobile 作为 value,存入 redis 并设置过期时间(注意这一步将用户手机号,也存入 Redis 是为了防止用户在获取完邮箱验证码后修改手机号,这一点很重要,很多开发同学都忘了这一步)

  • 第七步,使用线程池异步发送验证码邮件

前端在调用完 /sendEmailCode 接口后,就可以拿到 emailKey。

这样等用户输入邮箱里的验证码后,点击注册按钮,我们就可能正式开始注册操作了。

/registry 用户注册

@PostMapping("/registry")
public R registry(@RequestBody RegistryObj registryObj) {// 验证两次密码输入是否一致if (!StringUtils.equalsIgnoreCase(registryObj.getPassword(), registryObj.getConfirmPassword())) {return R.error(USER_TWO_PASSWORD_NOT_SAME_ERROR);}// 验证用户手机号是否唯一long count = iMemberService.count(Wrappers.lambdaQuery(Member.class).eq(Member::getMobile, registryObj.getMobile()));if (count > 0) {return R.error(USER_PHONE_HAS_REGISTER_ERROR);}// 判断图形验证码String redisCaptchaCode = redisCache.getCacheObject(registryObj.getCaptchaKey());if (registryObj.getCaptchaCode() == null || !redisCaptchaCode.equals(registryObj.getCaptchaCode().trim().toLowerCase())) {return R.error(USER_CAPTCHA_CODE_ERROR);}// 判断邮箱验证码String value = redisCache.getCacheObject(registryObj.getEmailKey());String[] split = value.split("_");if (split.length < 2) {return R.error(ReturnCodeEnum.USER_EMAIL_CODE_ERROR);}String redisEmailCode = split[0];String mobile = split[1];// 判断发送邮箱验证码的手机号是否与用户当前传入手机号一致if (!StringUtils.equalsIgnoreCase(mobile, registryObj.getMobile())) {return R.error(ReturnCodeEnum.USER_REGISTER_MOBILE_ERROR);}// 判断用户输入邮箱验证码是否正确if (registryObj.getEmailCode() == null || !redisEmailCode.equals(registryObj.getEmailCode().trim().toLowerCase())) {return R.error(ReturnCodeEnum.USER_EMAIL_CODE_ERROR);}// 删除验证码redisCache.deleteObject(registryObj.getCaptchaKey());redisCache.deleteObject(registryObj.getEmailKey());Member member = new Member();long time = System.currentTimeMillis();member.setNickname("昵称" + time / 1000);String avatar = SysConstants.DEFAULT_AVATAR;member.setAvatar(avatar);member.setMobile(registryObj.getMobile());member.setEmail(registryObj.getEmail());member.setPassword(SecurityUtils.encryptPassword(registryObj.getPassword()));member.setCreateTime(new Date());return R.result(iMemberService.save(member));
}

注册接口,需要逻辑完善,所以这里的校验逻辑会比较多,因为一个商城最重要的几个接口就是注册、登录、下单、支付等。

除了能让用户正常注册外,有时候还需要确保用户一个手机号只能注册一个账号,完成对用户手机号在商城的唯一性保障。除了先查询用户手机号是否已存在外,还需要对用户 member 表的手机号字段设置唯一索引来完成。注册接口讲解如下,

唯一索引可以防止用户重复点击注册按钮,保证一个手机号只能注册一个用户。

  • 第一步,验证用户输入两次密码是否一致

  • 第二步,验证用户输入的手机号是否唯一

  • 第三步,验证用户输入的图形验证码是否于 Redis 中存储一致

  • 第四步,验证发送邮箱验证码的手机号是否于 Redis 中存储一致

  • 第五步,验证用户输入的邮箱验证码是否于 Redis 中存储一致

  • 第六步,校验通过,开始删除图形验证码、邮箱验证码

  • 第七步,启动线程池,异步进行用户保存操作

最后聊两句

用户注册说简单是很简单,但是校验逻辑一定要做好!这是我的踩坑经验,现在我传授给你,希望能帮你平安🤝。

关注公众号【waynblog】每周分享技术干货、开源项目、实战经验、国外优质文章翻译等,您的关注将是我的更新动力!

相关文章:

用户注册这样玩,保你平安

前言 基本上每个系统系统都包含用户注册、发送验证码等基本操作。在前些年&#xff0c;我还记得我在逛 csdn、贴吧、网易新闻等网站的时候是可以不登陆也能浏览完网页内容的&#xff0c;但是近几年这些网站已经改成了不登陆不让用&#xff0c;浏览网页时不时提醒你要进行登录&…...

QXDM Filter使用指南

QXDM Filter使用指南 1. QXDM简介2 如何制作和导入Filter2.1 制作Filter2.1.1 制作Windows环境下Filter2.1.2 制作Linux环境下Filter 2.2 Windows环境下导入Filter 3 Filter配置3.1 注册拨号问题3.1.1 LOG Packets(OTA)3.1.2 LOG Packets3.1.3 Event Reports3.1.4 Message Pack…...

智能优化算法应用:基于蝴蝶算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蝴蝶算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蝴蝶算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蝴蝶算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…...

3dsMax插件Datasmith Exporter安装使用方法

3dsMax插件Datasmith Exporter安装使用方法 某些文件格式无法用Datasmith直接导入虚幻引擎&#xff0c;这些数据必须先被转换为Datasmith能够识别的文件格式。Datasmith Exporter插件就可以帮助您的软件导出可以被Datasmith导入虚幻引擎的.udatasmith格式文件。 在开始使用虚幻…...

Rocketmq架构

NameServer&#xff1a;作为注册中心&#xff0c;提供路由注册、路由踢出、路由发现功能&#xff0c;舍弃强一致&#xff0c;保证高可用&#xff0c;集群中各个节点不会实时通讯&#xff0c;其中一个节点下线之后&#xff0c;会提供另外一个节点保证路由功能。 Rocket mq name…...

中兴亮相中国国际现代化铁路技术装备展览会 筑智铁路5G同行

近日&#xff0c;第十六届中国国际现代化铁路技术装备展览会在北京中国国际展览中心举办&#xff0c;中兴以“数智铁路&#xff0c;5G同行”主题亮相本次展览会&#xff0c;并全面展示了“数字铁路网络基础设施”、“云边结合的铁路行业云”、“数字铁路赋能赋智”等方面的最新…...

从零学算法15

15.给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。 示例 …...

《Effective C++》条款27

尽量少做转型动作 class A { public:A(int x) :a(x) {};virtual void add(){a;} private:int a; }; class B :public A { public:B(int x) :b(x),A(x) {};virtual void add(){static_cast<A>(*this).add();b;}private:int b; }; 如上描述把子类转型为A类&#xff0c;调用…...

无图谱不AI之三元组数据保存Neo4j

目录 1. 代码学习1.1 源代码1.2 代码解读 没有图谱称为弱人工智能&#xff0c;有图谱的称为强人工智能。 图谱可以让机器学习和人工智能具备推理能力。 1. 代码学习 1.1 源代码 # -*- coding: utf-8 -*- from py2neo import Node, Graph, Relationship# 可以先阅读下文档&a…...

Mybatisplus同时向两张表里插入数据[事务的一致性]

一、需求&#xff1a;把靶器官的数据&#xff0c;单独拿出来作为一个从表&#xff0c;以List的方式接收这段数据&#xff1b; 此时分析&#xff0c;是需要有两个实体的&#xff0c;一个是主表的实体&#xff0c;一个是从表的实体&#xff0c;并在主表实体新增一个List 字段来接…...

天眼销:精准的企业名录

企业名录的重要性&#xff0c;对于销售而言都是极其重要的。本期为家人们分享如何正确挑选出优质的企业名录渠道&#xff0c;避免走一些弯弯坑坑。 为了有效利用企业名录进行客户开发&#xff0c;您需要关注信息的准确性、可提供的资源数量以及信息的时效性。能否根据您的需求…...

TypeError: Cannot read properties of null (reading ‘shapeFlag‘)

vue3 开发过程遇到这样一个报错 TypeError: Cannot read properties of null (reading shapeFlag)最后发现是ref定义的变量&#xff0c;在访问时没有使用.valuereactive 变量初始化是数组&#xff0c;如果使用字符串赋值时也会报这个错。...

视频监控平台EasyCVR多场景应用,AI视频分析技术助力行业升级转型

传统的视频监控系统建设&#xff0c;经常存在各方面的因素制约&#xff0c;造成管理机制不健全、统筹规划不到位、联网共享不规范&#xff0c;形成“信息孤岛”、“数据烟囱”。在监控系统的建设中缺乏统一规划&#xff0c;标准不统一、视频图像信息利用率低等问题日益突出。随…...

如何搭建外网可访问的Serv-U FTP服务器,轻松远程共享文件!

目录 1. 前言 2. 本地FTP搭建 2.1 Serv-U下载和安装 2.2 Serv-U共享网页测试 2.3 Cpolar下载和安装 3. 本地FTP发布 3.1 Cpolar云端设置 3.2 Cpolar本地设置 4. 公网访问测试 5. 总结 1. 前言 科技日益发展的今天&#xff0c;移动电子设备似乎成了我们生活的主角&am…...

c++--类型的基础

1.常量对象&#xff0c;常量成员函数 (1).常量对象 常量对象的引用和指针不能调用类的普通的成员函数。只能调用常量成员函数。 (2).常量成员函数&#xff1a;把const放在类成员函数参数列表后。表示隐含的this是一个指向常量的指针 (3).当创建一个const对象时&#xff0c;直到…...

Python with提前退出:坑与解决方案

Python with提前退出&#xff1a;坑与解决方案 问题的起源 早些时候使用with实现了一版全局进程锁&#xff0c;希望实现以下效果&#xff1a; Python with提前退出&#xff1a;坑与解决方案 全局进程锁本身不用多说&#xff0c;大部分都依靠外部的缓存来实现的&#xff0c;r…...

Vue3-provide和inject

作用和场景&#xff1a;顶层组件向任意的底层组件传递数据和方法&#xff0c;实现跨层组件通信 跨层传递普通数据&#xff1a; 1.顶层组件通过provide函数提供数据 2.底层组件通过inject函数获取数据 既可以传递普通数据&#xff0c;也可以使用ref传递响应式数据&#xff08…...

Python与设计模式--适配器模式

23种计模式之 前言 &#xff08;5&#xff09;单例模式、工厂模式、简单工厂模式、抽象工厂模式、建造者模式、原型模式、(7)代理模式、装饰器模式、适配器模式、门面模式、组合模式、享元模式、桥梁模式、&#xff08;11&#xff09;策略模式、责任链模式、命令模式、中介者模…...

大模型能否生成搜索引擎的未来?

文&#xff5c;郝 鑫 编&#xff5c;刘雨琦 ChatGPT火爆之前&#xff0c;水面下&#xff0c;也有中国公司也在朝着智能助手的方向努力。夸克便是其中之一。在GPT风靡科技圈后&#xff0c;国内就开始陆续冒出一些大模型厂商。对当时夸克而言&#xff0c;做大模型毋庸置疑&am…...

鸿蒙开发-ArkTS 语言-状态管理

[写在前面: 文章多处用到gif动图&#xff0c;如未自动播放&#xff0c;请点击图片] 衔接上一篇:鸿蒙开发-ArkTS 语言-基础语法 3. 状态管理 变量必须被装饰器装饰才能成为状态变量&#xff0c;状态变量的改变才能导致 UI 界面重新渲染 概念描述状态变量被状态装饰器装饰的变…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具&#xff0c;支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议&#xff0c;涵盖接口测试、性能测试、数字体验监测等测试类型…...